From 68deb2dbd9e6f7223dec07bfe390cb343016c24a Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Tue, 12 Dec 2023 17:45:17 +0600 Subject: [PATCH 001/111] Implement basic version of SM link generator --- .../test/phase0/fork_choice/test_generated.py | 319 ++++++++++++++++++ .../fork_choice_generated/README.md | 5 + .../generators/fork_choice_generated/main.py | 17 + .../fork_choice_generated/requirements.txt | 3 + 4 files changed, 344 insertions(+) create mode 100644 tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_generated.py create mode 100644 tests/generators/fork_choice_generated/README.md create mode 100644 tests/generators/fork_choice_generated/main.py create mode 100644 tests/generators/fork_choice_generated/requirements.txt diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_generated.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_generated.py new file mode 100644 index 0000000000..1a509782da --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_generated.py @@ -0,0 +1,319 @@ +import numpy as np +from eth2spec.test.context import ( + spec_state_test, + with_altair_and_later, + with_presets, +) +from eth2spec.test.helpers.constants import ( + MINIMAL, +) +from eth2spec.test.helpers.attestations import ( + next_epoch_with_attestations, + state_transition_with_full_block, + get_valid_attestation_at_slot, + get_valid_attestation, +) +from eth2spec.test.helpers.fork_choice import ( + get_genesis_forkchoice_store_and_block, + on_tick_and_append_step, + tick_and_add_block, +) +from eth2spec.test.helpers.state import ( + state_transition_and_sign_block, + transition_to, + next_slot, +) +from eth2spec.test.helpers.block import ( + build_empty_block_for_next_slot, +) + + +class SmLink(tuple): + @property + def source(self): + return self[0] + + @property + def target(self): + return self[1] + + +class BranchTip: + def __init__(self, beacon_state, attestations, participants): + self.beacon_state = beacon_state + self.attestations = attestations + self.participants = participants + + +def _justifying_participation_rate(): + # Hight particiaption rate is required to ensure high probability of justifying the target + return 95 + + +def _under_justifying_participation_rate(): + # Hight particiaption rate is required to ensure high probability of justifying the target + return 65 + + +def _create_new_branch_tip(spec, branch_tips: dict[SmLink:BranchTip], sm_link: SmLink) -> BranchTip: + tips_with_justified_source = [s for s in branch_tips.values() + if s.beacon_state.current_justified_checkpoint.epoch == + sm_link.source] + assert len(tips_with_justified_source) > 0 + + most_recent_tip = max(tips_with_justified_source, key=lambda s: s.beacon_state.slot) + return BranchTip(most_recent_tip.beacon_state.copy(), most_recent_tip.attestations.copy(), []) + + +def _compute_partitions(spec, branch_tips, current_links, current_epoch, rnd: np.random.Generator): + """ + Uniformly distributes active validators between branches to advance + O(N) complex -- N is a number of validators, might be inefficient with large validator sets + + Does not take into account validator's effective balance, based on assumption that EB is nearly the same + + :param spec: spec + :param branch_tips: {(source, target): tip_state} tip_states of all branches to process in an epoch + :param branches_to_advance: [(source, target)] a list of sm link branches that validators should be spread accross + :return: {(source, target): participants} + """ + + justification_targets = [l for l in current_links if l.target == current_epoch] + + # Justifying two different checkpoints isn't supported + assert len(justification_targets) < 2 + + # Case when there is just 1 link and there is no justification target + if len(current_links) == 1 and not any(justification_targets): + l = current_links[0] + state = branch_tips[l].beacon_state + active_validator_indices = spec.get_active_validator_indices(state, spec.get_current_epoch(state)) + participant_count = len(active_validator_indices) * _under_justifying_participation_rate() // 100 + return {l: rnd.permuted(active_validator_indices)[:participant_count]} + + participants = {l: [] for l in current_links} + + # Move the majority to the branch containing justification target + justifying_participants = [] + if any(justification_targets): + justification_target = justification_targets[0] + state = branch_tips[justification_target].beacon_state + active_validator_indices = spec.get_active_validator_indices(state, spec.get_current_epoch(state)) + participant_count = len(active_validator_indices) * _justifying_participation_rate() // 100 + justifying_participants = rnd.permuted(active_validator_indices)[:participant_count] + participants[justification_target] = justifying_participants + + # Case when the justification target is the only branch + if justification_targets == current_links: + return {justification_target: justifying_participants} + + # Genereral case + + # Collect a set of active validator indexes across all branches + active_validator_per_branch = {} + active_validators_total = set() + for l in current_links: + state = branch_tips[l].beacon_state + active_validator_per_branch[l] = spec.get_active_validator_indices(state, + spec.get_current_epoch(state)) + active_validators_total.update(active_validator_per_branch[l]) + + # remove selected participants from the active validators set + active_validators_total = active_validators_total.difference(justifying_participants) + + # For each index: + # 1) Collect a set of branches where the validators is in active state (except for justifying branch) + # 2) Append the index to the list of participants for a randomly selected branch + for index in active_validators_total: + active_branches = [l for l in current_links if + index in active_validator_per_branch[l] and l not in justification_targets] + participants[tuple(rnd.choice(active_branches))].append(index) + + return participants + + +def _advance_branch_to_next_epoch(spec, branch_tip, current_epoch): + def participants_filter(comm): + return [index for index in comm if index in branch_tip.participants] + + signed_blocks = [] + attestations = branch_tip.attestations.copy() + state = branch_tip.beacon_state.copy() + target_slot = spec.compute_start_slot_at_epoch(current_epoch + 1) + + while state.slot < target_slot: + proposer_index = spec.get_beacon_proposer_index(state) + if proposer_index in branch_tip.participants: + # Remove too old attestations (TODO change for Deneb and after) + attestations = [a for a in attestations if state.slot <= a.data.slot + spec.SLOTS_PER_EPOCH] + + # Prepare attestations + attestation_in_block = attestations[:spec.MAX_ATTESTATIONS] + attestations = attestations[spec.MAX_ATTESTATIONS:] + + # Create a block with attestations + block = build_empty_block_for_next_slot(spec, state) + for a in attestation_in_block: + block.body.attestations.append(a) + + # Run state transition and sign off on a block + signed_block = state_transition_and_sign_block(spec, state, block) + signed_blocks.append(signed_block) + else: + next_slot(spec, state) + + # Produce attestations to the previous slot + slot_to_attest = state.slot - 1 + committees_per_slot = spec.get_committee_count_per_slot(state, spec.compute_epoch_at_slot(slot_to_attest)) + attestations_in_slot = [] + for index in range(committees_per_slot): + beacon_committee = spec.get_beacon_committee(state, slot_to_attest, index) + participants = participants_filter(beacon_committee) + if len(participants) > 0: + attestation = get_valid_attestation( + spec, + state, + slot_to_attest, + index=index, + signed=True, + filter_participant_set=participants_filter + ) + attestations_in_slot.append(attestation) + + # And prepend them to the list + attestations = list(attestations_in_slot) + attestations + + # Clean up attestations that aren't eligible to be on chain anymore + attestations = [a for a in attestations if spec.compute_epoch_at_slot(a.data.slot) in ( + spec.get_current_epoch(state), spec.get_previous_epoch(state))] + + return signed_blocks, BranchTip(state, attestations, branch_tip.participants) + + +def _any_change_to_voting_partitions(spec, sm_links, current_epoch) -> bool: + assert current_epoch > spec.GENESIS_EPOCH + previous_epoch = current_epoch - 1 + + # Previous epoch is genesis one -- set up new partitions + if previous_epoch == spec.GENESIS_EPOCH: + return True + + # Previous or current epoch is justification target + # a) Previous epoch accounts for newly created branches or an even reshuffling between branches in current epoch + # b) Current epoch accounts for moving the majority to a branch with the justifying target + if any([l for l in sm_links if l.target == current_epoch or l.target == previous_epoch]): + return True + + # No new branch satisfying any SM link can be created without a new justified checkpoint from the previous epoch + # So partitions remain the same unless previous epoch has a justification + return False + + +def _generate_blocks(spec, initial_state, sm_links, rnd: np.random.Generator) -> []: + state = initial_state.copy() + + signed_blocks = [] + + # Sort sm_links + sm_links = sorted(sm_links) + + # Fill GENESIS_EPOCH with blocks + _, genesis_epoch_blocks, state = next_epoch_with_attestations(spec, state, True, False) + signed_blocks = signed_blocks + genesis_epoch_blocks + assert spec.get_current_epoch(state) == spec.compute_epoch_at_slot(spec.GENESIS_SLOT) + 1 + + # Initialize branch tips with the genesis tip + genesis_tip = BranchTip(state.copy(), [], [*range(0, len(state.validators))]) + branch_tips = {(spec.GENESIS_EPOCH, spec.GENESIS_EPOCH): genesis_tip} + + # Start from spec.GENESIS_EPOCH + 1 + lowest_epoch_boundary = spec.GENESIS_EPOCH + 1 + # Finish at after the highest justified target + highest_epoch_boundary = max(sm_links, key=lambda l: l.target).target + 1 + + # Skip to target + # 1. Skip with empty slots? + # 2. Skip with empty blocks? + # 3. Skip with partially full blocks? + # 4. Mix of 1, 2 and 3 + # 5. Pivot from existing chain or built from the checkpoint block? if many chains which one to use? + # and which epoch to start a skip from? + # with sm_links = [(0, 2), (0, 3)], (0, 3) has the following options: + # - (0, 2): 0 blocks, 0 blocks, full blocks | (0, 3): < 1/3 blocks + # + # Justify target + # 1. Checkpoint slot empty? + # 2. Missed blocks in the end of the target epoch? + for current_epoch in range(lowest_epoch_boundary, highest_epoch_boundary): + # Every epoch there are new randomly sampled partitions in the network + # If the current epoch is an SM link target then there is one major justifying patition and a number of small ones + # Attestations that are not included in the current epoch are left for inclusion in subsequent epochs + # We ensure high probability of justifying the target by setting high participation rate + # TODO: allow for partitioning in the middle of an epoch/epoch span when applicable + + # Advance only those branches that are required to build SM links in the future + # Branches that aren't containing a source of a future SM link won't be advanced + current_links = [l for l in sm_links if l.source < current_epoch <= l.target] + + # Initialize new branches + for l in (l for l in current_links if branch_tips.get(l) is None): + branch_tips[l] = _create_new_branch_tip(spec, branch_tips, l) + + # Reshuffle partitions if needed + if _any_change_to_voting_partitions(spec, sm_links, current_epoch): + partitions = _compute_partitions(spec, branch_tips, current_links, current_epoch, rnd) + for l in partitions.keys(): + old_tip_state = branch_tips[l] + new_tip_state = BranchTip(old_tip_state.beacon_state, old_tip_state.attestations, partitions[l]) + branch_tips[l] = new_tip_state + + # Advance every branch taking into account attestations from the previous epochs and voting partitions + for l in current_links: + branch_tip = branch_tips[l] + new_signed_blocks, new_branch_tip = _advance_branch_to_next_epoch(spec, branch_tip, current_epoch) + + post_state = new_branch_tip.beacon_state + assert spec.get_current_epoch(post_state) == current_epoch + 1 + if l.target == current_epoch: + assert post_state.previous_justified_checkpoint.epoch == l.source + assert post_state.current_justified_checkpoint.epoch == l.target + else: + assert post_state.current_justified_checkpoint.epoch == l.source + + branch_tips[l] = new_branch_tip + signed_blocks = signed_blocks + new_signed_blocks + + return sorted(signed_blocks, key=lambda b: b.message.slot) + + +@with_altair_and_later +@spec_state_test +@with_presets([MINIMAL], reason="too slow") +def test_generated_sm_links(spec, state): + """ + Creates a tree of supermajority links + """ + input = [(2, 3), (2, 4), (3, 6), (3, 5), (0, 2)] + seed = 128 + + rnd = np.random.default_rng(seed) + sm_links = [SmLink(l) for l in input] + signed_blocks = _generate_blocks(spec, state, sm_links, rnd) + + test_steps = [] + # Store initialization + store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) + yield 'anchor_state', state + yield 'anchor_block', anchor_block + current_time = state.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time + on_tick_and_append_step(spec, store, current_time, test_steps) + assert store.time == current_time + + # Apply generated blocks + for signed_block in signed_blocks: + block = signed_block.message + yield from tick_and_add_block(spec, store, signed_block, test_steps) + block_root = block.hash_tree_root() + assert store.blocks[block_root] == block + + yield 'steps', test_steps diff --git a/tests/generators/fork_choice_generated/README.md b/tests/generators/fork_choice_generated/README.md new file mode 100644 index 0000000000..e67b115ba1 --- /dev/null +++ b/tests/generators/fork_choice_generated/README.md @@ -0,0 +1,5 @@ +# Fork choice tests + +Fork choice tests cover the different forking cases with fork choice helper functions. + +Information on the format of the tests can be found in the [fork choice test formats documentation](../../formats/fork_choice/README.md). diff --git a/tests/generators/fork_choice_generated/main.py b/tests/generators/fork_choice_generated/main.py new file mode 100644 index 0000000000..59120fa9c5 --- /dev/null +++ b/tests/generators/fork_choice_generated/main.py @@ -0,0 +1,17 @@ +from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods +from eth2spec.test.helpers.constants import ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110 + + +if __name__ == "__main__": + generated_modes = {key: 'eth2spec.test.phase0.fork_choice.test_' + key for key in [ + 'generated', + ]} + + fork_choice_compliance_testing_modes = { + ALTAIR: generated_modes, + BELLATRIX: generated_modes, + CAPELLA: generated_modes, + DENEB: generated_modes + } + + run_state_test_generators(runner_name="fork_choice_generated", all_mods=fork_choice_compliance_testing_modes) diff --git a/tests/generators/fork_choice_generated/requirements.txt b/tests/generators/fork_choice_generated/requirements.txt new file mode 100644 index 0000000000..91e0b43b7c --- /dev/null +++ b/tests/generators/fork_choice_generated/requirements.txt @@ -0,0 +1,3 @@ +pytest>=4.4 +numpy>=1.26.2 +../../../[generator] From f989f37979d755d648191add07b35d98139f3144 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Mon, 18 Dec 2023 13:21:51 +0600 Subject: [PATCH 002/111] Remove numpy as a dependency --- .../test/phase0/fork_choice/test_generated.py | 70 ++++++++++++------- .../fork_choice_generated/requirements.txt | 1 - 2 files changed, 45 insertions(+), 26 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_generated.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_generated.py index 1a509782da..5797cbf022 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_generated.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_generated.py @@ -1,4 +1,4 @@ -import numpy as np +import random from eth2spec.test.context import ( spec_state_test, with_altair_and_later, @@ -9,6 +9,7 @@ ) from eth2spec.test.helpers.attestations import ( next_epoch_with_attestations, + next_slots_with_attestations, state_transition_with_full_block, get_valid_attestation_at_slot, get_valid_attestation, @@ -25,6 +26,8 @@ ) from eth2spec.test.helpers.block import ( build_empty_block_for_next_slot, + build_empty_block, + sign_block, ) @@ -65,7 +68,7 @@ def _create_new_branch_tip(spec, branch_tips: dict[SmLink:BranchTip], sm_link: S return BranchTip(most_recent_tip.beacon_state.copy(), most_recent_tip.attestations.copy(), []) -def _compute_partitions(spec, branch_tips, current_links, current_epoch, rnd: np.random.Generator): +def _compute_partitions(spec, branch_tips, current_links, current_epoch, rnd: random.Random): """ Uniformly distributes active validators between branches to advance O(N) complex -- N is a number of validators, might be inefficient with large validator sets @@ -89,7 +92,7 @@ def _compute_partitions(spec, branch_tips, current_links, current_epoch, rnd: np state = branch_tips[l].beacon_state active_validator_indices = spec.get_active_validator_indices(state, spec.get_current_epoch(state)) participant_count = len(active_validator_indices) * _under_justifying_participation_rate() // 100 - return {l: rnd.permuted(active_validator_indices)[:participant_count]} + return {l: rnd.sample(active_validator_indices, participant_count)} participants = {l: [] for l in current_links} @@ -100,7 +103,7 @@ def _compute_partitions(spec, branch_tips, current_links, current_epoch, rnd: np state = branch_tips[justification_target].beacon_state active_validator_indices = spec.get_active_validator_indices(state, spec.get_current_epoch(state)) participant_count = len(active_validator_indices) * _justifying_participation_rate() // 100 - justifying_participants = rnd.permuted(active_validator_indices)[:participant_count] + justifying_participants = rnd.sample(active_validator_indices, participant_count) participants[justification_target] = justifying_participants # Case when the justification target is the only branch @@ -142,8 +145,9 @@ def participants_filter(comm): target_slot = spec.compute_start_slot_at_epoch(current_epoch + 1) while state.slot < target_slot: - proposer_index = spec.get_beacon_proposer_index(state) - if proposer_index in branch_tip.participants: + # Produce a block if the proposer is in the network partition building a branch + if spec.get_beacon_proposer_index(state) in branch_tip.participants and state.slot > spec.GENESIS_SLOT: + # Remove too old attestations (TODO change for Deneb and after) attestations = [a for a in attestations if state.slot <= a.data.slot + spec.SLOTS_PER_EPOCH] @@ -152,18 +156,18 @@ def participants_filter(comm): attestations = attestations[spec.MAX_ATTESTATIONS:] # Create a block with attestations - block = build_empty_block_for_next_slot(spec, state) + block = build_empty_block(spec, state) for a in attestation_in_block: block.body.attestations.append(a) # Run state transition and sign off on a block - signed_block = state_transition_and_sign_block(spec, state, block) + spec.process_block(state, block) + block.state_root = state.hash_tree_root() + signed_block = sign_block(spec, state, block) signed_blocks.append(signed_block) - else: - next_slot(spec, state) - # Produce attestations to the previous slot - slot_to_attest = state.slot - 1 + # Produce attestations + slot_to_attest = state.slot committees_per_slot = spec.get_committee_count_per_slot(state, spec.compute_epoch_at_slot(slot_to_attest)) attestations_in_slot = [] for index in range(committees_per_slot): @@ -183,6 +187,9 @@ def participants_filter(comm): # And prepend them to the list attestations = list(attestations_in_slot) + attestations + # Advance a slot + next_slot(spec, state) + # Clean up attestations that aren't eligible to be on chain anymore attestations = [a for a in attestations if spec.compute_epoch_at_slot(a.data.slot) in ( spec.get_current_epoch(state), spec.get_previous_epoch(state))] @@ -191,7 +198,9 @@ def participants_filter(comm): def _any_change_to_voting_partitions(spec, sm_links, current_epoch) -> bool: - assert current_epoch > spec.GENESIS_EPOCH + if current_epoch == spec.GENESIS_EPOCH: + return True + previous_epoch = current_epoch - 1 # Previous epoch is genesis one -- set up new partitions @@ -209,7 +218,7 @@ def _any_change_to_voting_partitions(spec, sm_links, current_epoch) -> bool: return False -def _generate_blocks(spec, initial_state, sm_links, rnd: np.random.Generator) -> []: +def _generate_blocks(spec, initial_state, sm_links, rnd: random.Random) -> []: state = initial_state.copy() signed_blocks = [] @@ -218,18 +227,24 @@ def _generate_blocks(spec, initial_state, sm_links, rnd: np.random.Generator) -> sm_links = sorted(sm_links) # Fill GENESIS_EPOCH with blocks - _, genesis_epoch_blocks, state = next_epoch_with_attestations(spec, state, True, False) - signed_blocks = signed_blocks + genesis_epoch_blocks - assert spec.get_current_epoch(state) == spec.compute_epoch_at_slot(spec.GENESIS_SLOT) + 1 + # _, genesis_epoch_blocks, state = next_slots_with_attestations( + # spec, + # state, + # spec.SLOTS_PER_EPOCH - 1, + # True, + # False + # ) + # next_slot(spec, state) + # TODO: fill two slots with attestations or start branching from GENESIS? + # signed_blocks = signed_blocks + genesis_epoch_blocks + # assert spec.get_current_epoch(state) == spec.compute_epoch_at_slot(spec.GENESIS_SLOT) + 1 # Initialize branch tips with the genesis tip - genesis_tip = BranchTip(state.copy(), [], [*range(0, len(state.validators))]) + genesis_tip = BranchTip(state.copy(), [], []) branch_tips = {(spec.GENESIS_EPOCH, spec.GENESIS_EPOCH): genesis_tip} - # Start from spec.GENESIS_EPOCH + 1 - lowest_epoch_boundary = spec.GENESIS_EPOCH + 1 # Finish at after the highest justified target - highest_epoch_boundary = max(sm_links, key=lambda l: l.target).target + 1 + highest_target = max(sm_links, key=lambda l: l.target).target # Skip to target # 1. Skip with empty slots? @@ -244,7 +259,7 @@ def _generate_blocks(spec, initial_state, sm_links, rnd: np.random.Generator) -> # Justify target # 1. Checkpoint slot empty? # 2. Missed blocks in the end of the target epoch? - for current_epoch in range(lowest_epoch_boundary, highest_epoch_boundary): + for current_epoch in range(spec.GENESIS_EPOCH, highest_target + 1): # Every epoch there are new randomly sampled partitions in the network # If the current epoch is an SM link target then there is one major justifying patition and a number of small ones # Attestations that are not included in the current epoch are left for inclusion in subsequent epochs @@ -283,6 +298,11 @@ def _generate_blocks(spec, initial_state, sm_links, rnd: np.random.Generator) -> branch_tips[l] = new_branch_tip signed_blocks = signed_blocks + new_signed_blocks + # Add the last block to the top most branch + branch_tip = branch_tips[max(sm_links, key=lambda l: l.target)] + new_signed_blocks, _ = _advance_branch_to_next_epoch(spec, branch_tip, highest_target + 1) + signed_blocks = signed_blocks + new_signed_blocks + return sorted(signed_blocks, key=lambda b: b.message.slot) @@ -293,10 +313,10 @@ def test_generated_sm_links(spec, state): """ Creates a tree of supermajority links """ - input = [(2, 3), (2, 4), (3, 6), (3, 5), (0, 2)] - seed = 128 + input = [(2, 3), (2, 4), (3, 8), (3, 7), (0, 2)] + seed = 1 - rnd = np.random.default_rng(seed) + rnd = random.Random(seed) sm_links = [SmLink(l) for l in input] signed_blocks = _generate_blocks(spec, state, sm_links, rnd) diff --git a/tests/generators/fork_choice_generated/requirements.txt b/tests/generators/fork_choice_generated/requirements.txt index 91e0b43b7c..1822486863 100644 --- a/tests/generators/fork_choice_generated/requirements.txt +++ b/tests/generators/fork_choice_generated/requirements.txt @@ -1,3 +1,2 @@ pytest>=4.4 -numpy>=1.26.2 ../../../[generator] From 967445fbe25fe0b33048a32995573aee5ff7db2d Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Mon, 18 Dec 2023 18:30:40 +0600 Subject: [PATCH 003/111] Clean up generator's code --- .../test/phase0/fork_choice/test_generated.py | 377 ++++++++++-------- 1 file changed, 217 insertions(+), 160 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_generated.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_generated.py index 5797cbf022..cafbe0806d 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_generated.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_generated.py @@ -30,6 +30,12 @@ sign_block, ) +MAX_JUSTIFICATION_RATE = 99 +MIN_JUSTIFICATION_RATE = 91 + +MAX_UNDERJUSTIFICATION_RATE = 70 +MIN_UNDERJUSTIFICATION_RATE = 55 + class SmLink(tuple): @property @@ -48,94 +54,186 @@ def __init__(self, beacon_state, attestations, participants): self.participants = participants -def _justifying_participation_rate(): - # Hight particiaption rate is required to ensure high probability of justifying the target - return 95 +def _justifying_participation_rate(rnd: random.Random): + """ + Should be high enough to ensure justification happens + """ + return rnd.randint(MIN_JUSTIFICATION_RATE, MAX_JUSTIFICATION_RATE) -def _under_justifying_participation_rate(): - # Hight particiaption rate is required to ensure high probability of justifying the target - return 65 +def _under_justifying_participation_rate(rnd: random.Random): + return rnd.randint(MIN_UNDERJUSTIFICATION_RATE, MAX_UNDERJUSTIFICATION_RATE) def _create_new_branch_tip(spec, branch_tips: dict[SmLink:BranchTip], sm_link: SmLink) -> BranchTip: + """ + Initialized a branch tip state for a new branch satisfying the given sm_link. + :return: a new branch tip. + """ + + # Find all forks with justified source tips_with_justified_source = [s for s in branch_tips.values() if s.beacon_state.current_justified_checkpoint.epoch == sm_link.source] assert len(tips_with_justified_source) > 0 + # Find and return the most adanced one most_recent_tip = max(tips_with_justified_source, key=lambda s: s.beacon_state.slot) return BranchTip(most_recent_tip.beacon_state.copy(), most_recent_tip.attestations.copy(), []) -def _compute_partitions(spec, branch_tips, current_links, current_epoch, rnd: random.Random): - """ - Uniformly distributes active validators between branches to advance - O(N) complex -- N is a number of validators, might be inefficient with large validator sets +def _sample_validator_partition(spec, state, epoch, participation_rate, rnd) -> []: + active_validator_indices = spec.get_active_validator_indices(state, epoch) + participants_count = len(active_validator_indices) * participation_rate // 100 + return rnd.sample(active_validator_indices, participants_count) - Does not take into account validator's effective balance, based on assumption that EB is nearly the same - :param spec: spec - :param branch_tips: {(source, target): tip_state} tip_states of all branches to process in an epoch - :param branches_to_advance: [(source, target)] a list of sm link branches that validators should be spread accross - :return: {(source, target): participants} +def _compute_validator_partitions(spec, branch_tips, current_links, current_epoch, rnd: random.Random) -> dict[ + SmLink:[int]]: + """ + Note: O(N) complex (N is a number of validators) and might be inefficient with large validator sets + + Uniformly distributes active validators between active forks specified by a given set of sm_links. + Handles two cases: + 1. Single active fork: + Randomly sample a single validator partition taking into account + whether the fork should have a justified checkpoint in the current epoch. + 2. Multiple active forks: + i. sample the majority partition if one of the forks is about to justify during the current epoch, + ii. run through a set of active validators and randomly select a fork for it, + do no consider validators that were sampled into the majority partition. + + Does not take into account validator's effective balance, based on assumption that the EB of every validator + is nearly the same. + + :return: [SmLink: participants] """ - justification_targets = [l for l in current_links if l.target == current_epoch] + justifying_links = [l for l in current_links if l.target == current_epoch] - # Justifying two different checkpoints isn't supported - assert len(justification_targets) < 2 + # Justifying conflicting checkpoints isn't supported + assert len(justifying_links) < 2 + justifying_link = justifying_links[0] if any(justifying_links) else None - # Case when there is just 1 link and there is no justification target - if len(current_links) == 1 and not any(justification_targets): - l = current_links[0] - state = branch_tips[l].beacon_state - active_validator_indices = spec.get_active_validator_indices(state, spec.get_current_epoch(state)) - participant_count = len(active_validator_indices) * _under_justifying_participation_rate() // 100 - return {l: rnd.sample(active_validator_indices, participant_count)} + # Sanity check + for sm_link in current_links: + assert spec.get_current_epoch(branch_tips[sm_link].beacon_state) == current_epoch + + # Case when there is just one active fork + if len(current_links) == 1: + the_sm_link = current_links[0] + + if the_sm_link == justifying_link: + participation_rate = _justifying_participation_rate(rnd) + else: + participation_rate = _under_justifying_participation_rate(rnd) + + state = branch_tips[the_sm_link].beacon_state + participants = _sample_validator_partition(spec, state, current_epoch, participation_rate, rnd) + + return {the_sm_link: participants} + # Cases with more than one active fork participants = {l: [] for l in current_links} # Move the majority to the branch containing justification target justifying_participants = [] - if any(justification_targets): - justification_target = justification_targets[0] - state = branch_tips[justification_target].beacon_state - active_validator_indices = spec.get_active_validator_indices(state, spec.get_current_epoch(state)) - participant_count = len(active_validator_indices) * _justifying_participation_rate() // 100 - justifying_participants = rnd.sample(active_validator_indices, participant_count) - participants[justification_target] = justifying_participants + if justifying_link is not None: + state = branch_tips[justifying_link].beacon_state + justifying_participants = _sample_validator_partition(spec, + branch_tips[justifying_link].beacon_state, + current_epoch, + _justifying_participation_rate(rnd), + rnd) - # Case when the justification target is the only branch - if justification_targets == current_links: - return {justification_target: justifying_participants} + participants[justifying_link] = justifying_participants - # Genereral case - - # Collect a set of active validator indexes across all branches + # Collect a set of active validator indexes across all forks active_validator_per_branch = {} - active_validators_total = set() + all_active_validators = set() for l in current_links: state = branch_tips[l].beacon_state - active_validator_per_branch[l] = spec.get_active_validator_indices(state, - spec.get_current_epoch(state)) - active_validators_total.update(active_validator_per_branch[l]) + active_validator_per_branch[l] = spec.get_active_validator_indices(state, current_epoch) + all_active_validators.update(active_validator_per_branch[l]) - # remove selected participants from the active validators set - active_validators_total = active_validators_total.difference(justifying_participants) + # Remove validators selected for justifying brach from the pool of active participants + all_active_validators = all_active_validators.difference(justifying_participants) # For each index: # 1) Collect a set of branches where the validators is in active state (except for justifying branch) # 2) Append the index to the list of participants for a randomly selected branch - for index in active_validators_total: + for index in all_active_validators: active_branches = [l for l in current_links if - index in active_validator_per_branch[l] and l not in justification_targets] + index in active_validator_per_branch[l] and l not in justifying_links] participants[tuple(rnd.choice(active_branches))].append(index) return participants +def _produce_block(spec, state, attestations): + """ + Produces a block including as many attestations as it is possible. + :return: Signed block, the post block state and attestations that were not included into the block. + """ + + # Filter out too old attestastions (TODO relax condition for Deneb) + eligible_attestations = [a for a in attestations if state.slot <= a.data.slot + spec.SLOTS_PER_EPOCH] + + # Prepare attestations + attestation_in_block = eligible_attestations[:spec.MAX_ATTESTATIONS] + + # Create a block with attestations + block = build_empty_block(spec, state) + for a in attestation_in_block: + block.body.attestations.append(a) + + # Run state transition and sign off on a block + post_state = state.copy() + spec.process_block(post_state, block) + block.state_root = post_state.hash_tree_root() + signed_block = sign_block(spec, post_state, block) + + not_included_attestations = [a for a in attestations if a not in attestation_in_block] + + return signed_block, post_state, not_included_attestations + + +def _attest_to_slot(spec, state, slot_to_attest, participants_filter): + """ + Creates attestation is a slot respecting participating validators. + :return: produced attestations + """ + + assert slot_to_attest <= state.slot + + committees_per_slot = spec.get_committee_count_per_slot(state, spec.compute_epoch_at_slot(slot_to_attest)) + attestations_in_slot = [] + for index in range(committees_per_slot): + beacon_committee = spec.get_beacon_committee(state, slot_to_attest, index) + participants = participants_filter(beacon_committee) + if any(participants): + attestation = get_valid_attestation( + spec, + state, + slot_to_attest, + index=index, + signed=True, + filter_participant_set=participants_filter + ) + attestations_in_slot.append(attestation) + + return attestations_in_slot + + def _advance_branch_to_next_epoch(spec, branch_tip, current_epoch): + """ + Advances a state of the block tree branch to the next epoch + respecting validators participanting in building and attesting to this branch. + + The returned beacon state is advanced to the first slot of the next epoch while no block for that slot is created, + produced attestations that aren't yet included on chain are preserved for the future inclusion. + """ + def participants_filter(comm): return [index for index in comm if index in branch_tip.participants] @@ -145,164 +243,123 @@ def participants_filter(comm): target_slot = spec.compute_start_slot_at_epoch(current_epoch + 1) while state.slot < target_slot: - # Produce a block if the proposer is in the network partition building a branch - if spec.get_beacon_proposer_index(state) in branch_tip.participants and state.slot > spec.GENESIS_SLOT: - - # Remove too old attestations (TODO change for Deneb and after) - attestations = [a for a in attestations if state.slot <= a.data.slot + spec.SLOTS_PER_EPOCH] - - # Prepare attestations - attestation_in_block = attestations[:spec.MAX_ATTESTATIONS] - attestations = attestations[spec.MAX_ATTESTATIONS:] - - # Create a block with attestations - block = build_empty_block(spec, state) - for a in attestation_in_block: - block.body.attestations.append(a) - - # Run state transition and sign off on a block - spec.process_block(state, block) - block.state_root = state.hash_tree_root() - signed_block = sign_block(spec, state, block) + # Produce block if the proposer is among participanting validators + proposer = spec.get_beacon_proposer_index(state) + if state.slot > spec.GENESIS_SLOT and proposer in branch_tip.participants: + signed_block, state, attestations = _produce_block(spec, state, attestations) signed_blocks.append(signed_block) # Produce attestations - slot_to_attest = state.slot - committees_per_slot = spec.get_committee_count_per_slot(state, spec.compute_epoch_at_slot(slot_to_attest)) - attestations_in_slot = [] - for index in range(committees_per_slot): - beacon_committee = spec.get_beacon_committee(state, slot_to_attest, index) - participants = participants_filter(beacon_committee) - if len(participants) > 0: - attestation = get_valid_attestation( - spec, - state, - slot_to_attest, - index=index, - signed=True, - filter_participant_set=participants_filter - ) - attestations_in_slot.append(attestation) - + attestations_in_slot = _attest_to_slot(spec, state, state.slot, participants_filter) # And prepend them to the list attestations = list(attestations_in_slot) + attestations # Advance a slot next_slot(spec, state) - # Clean up attestations that aren't eligible to be on chain anymore - attestations = [a for a in attestations if spec.compute_epoch_at_slot(a.data.slot) in ( - spec.get_current_epoch(state), spec.get_previous_epoch(state))] + # Cleanup attestations by removing outdated ones + attestations = [a for a in attestations if + a.data.target in (spec.get_previous_epoch(state), spec.get_current_epoch(state))] return signed_blocks, BranchTip(state, attestations, branch_tip.participants) -def _any_change_to_voting_partitions(spec, sm_links, current_epoch) -> bool: +def _any_change_to_validator_partitions(spec, sm_links, current_epoch) -> bool: + """ + Returns ``true`` if validator partitions should be re-shuffled to advance branches in the current epoch: + 1. Genesis epoch always requires a new shuffling. + 2. Previous epoch has justified checkpoints. + Therefore, new supermajority links may need to be processed during the current epoch, + thus new block tree branches with new validator partitions may need to be created. + 3. Current epoch has justified checkpoint. + Therefore, the majority of validators must be moved to the justifying branch. + """ + if current_epoch == spec.GENESIS_EPOCH: return True previous_epoch = current_epoch - 1 + for l in sm_links: + if l.target == current_epoch or l.target == previous_epoch: + return True - # Previous epoch is genesis one -- set up new partitions - if previous_epoch == spec.GENESIS_EPOCH: - return True - - # Previous or current epoch is justification target - # a) Previous epoch accounts for newly created branches or an even reshuffling between branches in current epoch - # b) Current epoch accounts for moving the majority to a branch with the justifying target - if any([l for l in sm_links if l.target == current_epoch or l.target == previous_epoch]): - return True - - # No new branch satisfying any SM link can be created without a new justified checkpoint from the previous epoch - # So partitions remain the same unless previous epoch has a justification return False -def _generate_blocks(spec, initial_state, sm_links, rnd: random.Random) -> []: - state = initial_state.copy() +def _generate_blocks(spec, anchor_state, sm_links, rnd: random.Random) -> []: + """ + Generates a sequence of blocks satisfying a tree of supermajority links specified in the sm_links list, + i.e. a sequence of blocks with attestations required to create given supermajority links. + + The block generation strategy is to run through a span of epochs covered by the supermajority links + and for each epoch of the span apply the following steps: + 1. Obtain a list of supermajority links covering the epoch. + 2. Create a new block tree branch (fork) for every newly observed supermajority link. + 3. Randomly sample all active validators between a set of forks that are being advanced in the epoch. + Validator partitions are disjoint and are changing only at the epoch boundary. + If no new branches are created in the current epoch then partitions from the previous epoch will be used + to advahce the state of every fork to the next epoch. + 4. Advance every fork to the next epoch respecting a validator partition assigned to it in the current epoch. + Preserve attestations produced but not yet included on chain for potential inclusion in the next epoch. + 5. Justify required checkpoints by moving the majority of validators to the justifying fork, + this is taken into account by step (3). + + :return: Sequence of signed blocks oredered by a slot number. + """ + state = anchor_state.copy() signed_blocks = [] - # Sort sm_links - sm_links = sorted(sm_links) - - # Fill GENESIS_EPOCH with blocks - # _, genesis_epoch_blocks, state = next_slots_with_attestations( - # spec, - # state, - # spec.SLOTS_PER_EPOCH - 1, - # True, - # False - # ) - # next_slot(spec, state) - # TODO: fill two slots with attestations or start branching from GENESIS? - # signed_blocks = signed_blocks + genesis_epoch_blocks - # assert spec.get_current_epoch(state) == spec.compute_epoch_at_slot(spec.GENESIS_SLOT) + 1 - + # branch_tips hold the most recent state, validator partition and not included attestations for every fork # Initialize branch tips with the genesis tip genesis_tip = BranchTip(state.copy(), [], []) branch_tips = {(spec.GENESIS_EPOCH, spec.GENESIS_EPOCH): genesis_tip} - # Finish at after the highest justified target - highest_target = max(sm_links, key=lambda l: l.target).target - - # Skip to target - # 1. Skip with empty slots? - # 2. Skip with empty blocks? - # 3. Skip with partially full blocks? - # 4. Mix of 1, 2 and 3 - # 5. Pivot from existing chain or built from the checkpoint block? if many chains which one to use? - # and which epoch to start a skip from? - # with sm_links = [(0, 2), (0, 3)], (0, 3) has the following options: - # - (0, 2): 0 blocks, 0 blocks, full blocks | (0, 3): < 1/3 blocks - # - # Justify target - # 1. Checkpoint slot empty? - # 2. Missed blocks in the end of the target epoch? - for current_epoch in range(spec.GENESIS_EPOCH, highest_target + 1): - # Every epoch there are new randomly sampled partitions in the network - # If the current epoch is an SM link target then there is one major justifying patition and a number of small ones - # Attestations that are not included in the current epoch are left for inclusion in subsequent epochs - # We ensure high probability of justifying the target by setting high participation rate - # TODO: allow for partitioning in the middle of an epoch/epoch span when applicable - - # Advance only those branches that are required to build SM links in the future - # Branches that aren't containing a source of a future SM link won't be advanced - current_links = [l for l in sm_links if l.source < current_epoch <= l.target] - - # Initialize new branches - for l in (l for l in current_links if branch_tips.get(l) is None): + highest_target_sm_link = max(sm_links, key=lambda l: l.target) + + # Finish at after the highest justified checkpoint + for current_epoch in range(spec.GENESIS_EPOCH, highest_target_sm_link.target + 1): + # Obtain sm links including current epoch + current_epoch_sm_links = [l for l in sm_links if l.source < current_epoch <= l.target] + + # Initialize new forks + for l in (l for l in current_epoch_sm_links if branch_tips.get(l) is None): branch_tips[l] = _create_new_branch_tip(spec, branch_tips, l) # Reshuffle partitions if needed - if _any_change_to_voting_partitions(spec, sm_links, current_epoch): - partitions = _compute_partitions(spec, branch_tips, current_links, current_epoch, rnd) + if _any_change_to_validator_partitions(spec, sm_links, current_epoch): + partitions = _compute_validator_partitions(spec, branch_tips, current_epoch_sm_links, current_epoch, rnd) for l in partitions.keys(): old_tip_state = branch_tips[l] new_tip_state = BranchTip(old_tip_state.beacon_state, old_tip_state.attestations, partitions[l]) branch_tips[l] = new_tip_state - # Advance every branch taking into account attestations from the previous epochs and voting partitions - for l in current_links: - branch_tip = branch_tips[l] + # Advance every branch taking into account attestations from past epochs and voting partitions + for sm_link in current_epoch_sm_links: + branch_tip = branch_tips[sm_link] new_signed_blocks, new_branch_tip = _advance_branch_to_next_epoch(spec, branch_tip, current_epoch) + # Run sanity checks post_state = new_branch_tip.beacon_state assert spec.get_current_epoch(post_state) == current_epoch + 1 - if l.target == current_epoch: - assert post_state.previous_justified_checkpoint.epoch == l.source - assert post_state.current_justified_checkpoint.epoch == l.target + if sm_link.target == current_epoch: + assert post_state.previous_justified_checkpoint.epoch == sm_link.source + assert post_state.current_justified_checkpoint.epoch == sm_link.target else: - assert post_state.current_justified_checkpoint.epoch == l.source + assert post_state.current_justified_checkpoint.epoch == sm_link.source - branch_tips[l] = new_branch_tip - signed_blocks = signed_blocks + new_signed_blocks + # Create the first block of the next epoch to realize justification on chain + # Required only when the fork won't be advanced in any of the future epochs + is_fork_advanced_in_future = any((l for l in sm_links if l.source == sm_link.target)) + if sm_link.target == current_epoch and not is_fork_advanced_in_future: + tip_block, _, _ = _produce_block(spec, new_branch_tip.beacon_state, new_branch_tip.attestations) + new_signed_blocks.append(tip_block) - # Add the last block to the top most branch - branch_tip = branch_tips[max(sm_links, key=lambda l: l.target)] - new_signed_blocks, _ = _advance_branch_to_next_epoch(spec, branch_tip, highest_target + 1) - signed_blocks = signed_blocks + new_signed_blocks + # Store the result + branch_tips[sm_link] = new_branch_tip + signed_blocks = signed_blocks + new_signed_blocks + # Sort blocks by a slot return sorted(signed_blocks, key=lambda b: b.message.slot) From 2101e9f50638264e8eae6c3fd67c5cf2417d3ed8 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Mon, 18 Dec 2023 19:32:27 +0600 Subject: [PATCH 004/111] Add debug output --- .../test/phase0/fork_choice/test_generated.py | 58 ++++++++++++++++--- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_generated.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_generated.py index cafbe0806d..80ad05dcc9 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_generated.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_generated.py @@ -264,10 +264,10 @@ def participants_filter(comm): return signed_blocks, BranchTip(state, attestations, branch_tip.participants) -def _any_change_to_validator_partitions(spec, sm_links, current_epoch) -> bool: +def _any_change_to_validator_partitions(spec, sm_links, current_epoch, anchor_epoch) -> bool: """ Returns ``true`` if validator partitions should be re-shuffled to advance branches in the current epoch: - 1. Genesis epoch always requires a new shuffling. + 1. Anchor epoch always requires a new shuffling. 2. Previous epoch has justified checkpoints. Therefore, new supermajority links may need to be processed during the current epoch, thus new block tree branches with new validator partitions may need to be created. @@ -275,7 +275,7 @@ def _any_change_to_validator_partitions(spec, sm_links, current_epoch) -> bool: Therefore, the majority of validators must be moved to the justifying branch. """ - if current_epoch == spec.GENESIS_EPOCH: + if current_epoch == anchor_epoch: return True previous_epoch = current_epoch - 1 @@ -286,7 +286,34 @@ def _any_change_to_validator_partitions(spec, sm_links, current_epoch) -> bool: return False -def _generate_blocks(spec, anchor_state, sm_links, rnd: random.Random) -> []: +def _print_block(spec, epoch_state, signed_block): + block = signed_block.message + attesters = set() + for a in block.body.attestations: + attesters.update(spec.get_attesting_indices(epoch_state, a.data, a.aggregation_bits)) + + attester_str = str(attesters) if any(attesters) else "{}" + + return "b(p: " + str(block.proposer_index) + ", a:" + attester_str + ")" + + +def _print_epoch(spec, epoch_state, signed_blocks): + epoch = spec.get_current_epoch(epoch_state) + start_slot = spec.compute_start_slot_at_epoch(epoch) + ret = "" + for slot in range(start_slot, start_slot + spec.SLOTS_PER_EPOCH + 1): + blocks_in_slot = [b for b in signed_blocks if b.message.slot == slot] + if ret != "": + ret = ret + " <- " + if any(blocks_in_slot): + ret = ret + "s(" + str(slot) + ", " + _print_block(spec, epoch_state, blocks_in_slot[0]) + ")" + else: + ret = ret + "s(" + str(slot) + ", _)" + + return ret + + +def _generate_blocks(spec, anchor_state, sm_links, rnd: random.Random, debug) -> []: """ Generates a sequence of blocks satisfying a tree of supermajority links specified in the sm_links list, i.e. a sequence of blocks with attestations required to create given supermajority links. @@ -315,25 +342,31 @@ def _generate_blocks(spec, anchor_state, sm_links, rnd: random.Random) -> []: genesis_tip = BranchTip(state.copy(), [], []) branch_tips = {(spec.GENESIS_EPOCH, spec.GENESIS_EPOCH): genesis_tip} + anchor_epoch = spec.GENESIS_EPOCH highest_target_sm_link = max(sm_links, key=lambda l: l.target) # Finish at after the highest justified checkpoint - for current_epoch in range(spec.GENESIS_EPOCH, highest_target_sm_link.target + 1): - # Obtain sm links including current epoch - current_epoch_sm_links = [l for l in sm_links if l.source < current_epoch <= l.target] + for current_epoch in range(anchor_epoch, highest_target_sm_link.target + 1): + # Obtain sm links that span over the current epoch + if current_epoch == anchor_epoch: + current_epoch_sm_links = [l for l in sm_links if l.source == anchor_epoch] + else: + current_epoch_sm_links = [l for l in sm_links if l.source < current_epoch <= l.target] # Initialize new forks for l in (l for l in current_epoch_sm_links if branch_tips.get(l) is None): branch_tips[l] = _create_new_branch_tip(spec, branch_tips, l) # Reshuffle partitions if needed - if _any_change_to_validator_partitions(spec, sm_links, current_epoch): + if _any_change_to_validator_partitions(spec, sm_links, current_epoch, anchor_epoch): partitions = _compute_validator_partitions(spec, branch_tips, current_epoch_sm_links, current_epoch, rnd) for l in partitions.keys(): old_tip_state = branch_tips[l] new_tip_state = BranchTip(old_tip_state.beacon_state, old_tip_state.attestations, partitions[l]) branch_tips[l] = new_tip_state + if debug: + print("\nepoch ", current_epoch, ":") # Advance every branch taking into account attestations from past epochs and voting partitions for sm_link in current_epoch_sm_links: branch_tip = branch_tips[sm_link] @@ -355,6 +388,12 @@ def _generate_blocks(spec, anchor_state, sm_links, rnd: random.Random) -> []: tip_block, _, _ = _produce_block(spec, new_branch_tip.beacon_state, new_branch_tip.attestations) new_signed_blocks.append(tip_block) + # Debug output + if debug: + print(sm_link, ":", _print_epoch(spec, branch_tips[sm_link].beacon_state, new_signed_blocks)) + print(" ", len(branch_tips[sm_link].participants), "participants:", + branch_tips[sm_link].participants) + # Store the result branch_tips[sm_link] = new_branch_tip signed_blocks = signed_blocks + new_signed_blocks @@ -372,10 +411,11 @@ def test_generated_sm_links(spec, state): """ input = [(2, 3), (2, 4), (3, 8), (3, 7), (0, 2)] seed = 1 + debug = False rnd = random.Random(seed) sm_links = [SmLink(l) for l in input] - signed_blocks = _generate_blocks(spec, state, sm_links, rnd) + signed_blocks = _generate_blocks(spec, state, sm_links, rnd, debug) test_steps = [] # Store initialization From 507bac8de54772de9ce8bb74a0c2bc3e097d6838 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Mon, 18 Dec 2023 20:46:25 +0600 Subject: [PATCH 005/111] Add anchor_epoch as a parameter and debug checks --- .../test/phase0/fork_choice/test_generated.py | 93 ++++++++++++++----- 1 file changed, 68 insertions(+), 25 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_generated.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_generated.py index 80ad05dcc9..5233051448 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_generated.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_generated.py @@ -267,18 +267,20 @@ def participants_filter(comm): def _any_change_to_validator_partitions(spec, sm_links, current_epoch, anchor_epoch) -> bool: """ Returns ``true`` if validator partitions should be re-shuffled to advance branches in the current epoch: - 1. Anchor epoch always requires a new shuffling. + 1. The first epoch after the anchor epoch always requires a new shuffling. 2. Previous epoch has justified checkpoints. Therefore, new supermajority links may need to be processed during the current epoch, thus new block tree branches with new validator partitions may need to be created. 3. Current epoch has justified checkpoint. Therefore, the majority of validators must be moved to the justifying branch. """ + assert current_epoch > anchor_epoch - if current_epoch == anchor_epoch: + previous_epoch = current_epoch - 1 + + if previous_epoch == anchor_epoch: return True - previous_epoch = current_epoch - 1 for l in sm_links: if l.target == current_epoch or l.target == previous_epoch: return True @@ -286,12 +288,17 @@ def _any_change_to_validator_partitions(spec, sm_links, current_epoch, anchor_ep return False -def _print_block(spec, epoch_state, signed_block): +def _attesters_in_block(spec, epoch_state, signed_block): block = signed_block.message attesters = set() for a in block.body.attestations: attesters.update(spec.get_attesting_indices(epoch_state, a.data, a.aggregation_bits)) + return attesters + +def _print_block(spec, epoch_state, signed_block): + block = signed_block.message + attesters = _attesters_in_block(spec, epoch_state, signed_block) attester_str = str(attesters) if any(attesters) else "{}" return "b(p: " + str(block.proposer_index) + ", a:" + attester_str + ")" @@ -313,7 +320,7 @@ def _print_epoch(spec, epoch_state, signed_blocks): return ret -def _generate_blocks(spec, anchor_state, sm_links, rnd: random.Random, debug) -> []: +def _generate_blocks(spec, genesis_state, sm_links, anchor_epoch, rnd: random.Random, debug) -> []: """ Generates a sequence of blocks satisfying a tree of supermajority links specified in the sm_links list, i.e. a sequence of blocks with attestations required to create given supermajority links. @@ -333,25 +340,28 @@ def _generate_blocks(spec, anchor_state, sm_links, rnd: random.Random, debug) -> :return: Sequence of signed blocks oredered by a slot number. """ - - state = anchor_state.copy() + state = genesis_state.copy() signed_blocks = [] + genesis_tip = BranchTip(state.copy(), [], [*range(0, len(state.validators))]) + + # Move the state to the anchor_epoch + anchor_tip = genesis_tip + for epoch in (spec.GENESIS_EPOCH, anchor_epoch): + new_signed_blocks, anchor_tip = _advance_branch_to_next_epoch(spec, anchor_tip, epoch) + signed_blocks = signed_blocks + new_signed_blocks + # branch_tips hold the most recent state, validator partition and not included attestations for every fork - # Initialize branch tips with the genesis tip - genesis_tip = BranchTip(state.copy(), [], []) - branch_tips = {(spec.GENESIS_EPOCH, spec.GENESIS_EPOCH): genesis_tip} + # Initialize branch tips with the anchor tip + anchor_link = SmLink((spec.GENESIS_EPOCH, anchor_epoch)) + branch_tips = {anchor_link: anchor_tip} - anchor_epoch = spec.GENESIS_EPOCH highest_target_sm_link = max(sm_links, key=lambda l: l.target) # Finish at after the highest justified checkpoint - for current_epoch in range(anchor_epoch, highest_target_sm_link.target + 1): + for current_epoch in range(anchor_epoch + 1, highest_target_sm_link.target + 1): # Obtain sm links that span over the current epoch - if current_epoch == anchor_epoch: - current_epoch_sm_links = [l for l in sm_links if l.source == anchor_epoch] - else: - current_epoch_sm_links = [l for l in sm_links if l.source < current_epoch <= l.target] + current_epoch_sm_links = [l for l in sm_links if l.source < current_epoch <= l.target] # Initialize new forks for l in (l for l in current_epoch_sm_links if branch_tips.get(l) is None): @@ -365,24 +375,41 @@ def _generate_blocks(spec, anchor_state, sm_links, rnd: random.Random, debug) -> new_tip_state = BranchTip(old_tip_state.beacon_state, old_tip_state.attestations, partitions[l]) branch_tips[l] = new_tip_state + # Debug checks if debug: print("\nepoch ", current_epoch, ":") + # Partitions are disjoint + for l1 in current_epoch_sm_links: + l1_participants = branch_tips[l1].participants + for l2 in current_epoch_sm_links: + if l1 != l2: + l2_participants = branch_tips[l2].participants + intersection = set(l1_participants).intersection(l2_participants) + assert len(intersection) == 0, \ + str(l1) + " and " + str(l2) + " has common participants: " + str(intersection) + # Advance every branch taking into account attestations from past epochs and voting partitions for sm_link in current_epoch_sm_links: branch_tip = branch_tips[sm_link] + assert spec.get_current_epoch(branch_tip.beacon_state) == current_epoch, \ + "Unexpected current_epoch(branch_tip.beacon_state)" new_signed_blocks, new_branch_tip = _advance_branch_to_next_epoch(spec, branch_tip, current_epoch) # Run sanity checks post_state = new_branch_tip.beacon_state - assert spec.get_current_epoch(post_state) == current_epoch + 1 + assert spec.get_current_epoch(post_state) == current_epoch + 1, \ + "Unexpected post_state epoch" if sm_link.target == current_epoch: - assert post_state.previous_justified_checkpoint.epoch == sm_link.source - assert post_state.current_justified_checkpoint.epoch == sm_link.target + assert post_state.previous_justified_checkpoint.epoch == sm_link.source, \ + "Unexpected previous_justified_checkpoint.epoch" + assert post_state.current_justified_checkpoint.epoch == sm_link.target, \ + "Unexpected current_justified_checkpoint.epoch" else: - assert post_state.current_justified_checkpoint.epoch == sm_link.source + assert post_state.current_justified_checkpoint.epoch == sm_link.source, \ + "Unexpected current_justified_checkpoint.epoch" - # Create the first block of the next epoch to realize justification on chain - # Required only when the fork won't be advanced in any of the future epochs + # Create the first block of the next epoch to realize justification on chain, + # neccessary only when the fork won't be advanced in any of the future epochs is_fork_advanced_in_future = any((l for l in sm_links if l.source == sm_link.target)) if sm_link.target == current_epoch and not is_fork_advanced_in_future: tip_block, _, _ = _produce_block(spec, new_branch_tip.beacon_state, new_branch_tip.attestations) @@ -394,6 +421,21 @@ def _generate_blocks(spec, anchor_state, sm_links, rnd: random.Random, debug) -> print(" ", len(branch_tips[sm_link].participants), "participants:", branch_tips[sm_link].participants) + # Debug checks + if debug: + # Proposers are aligned with the partition + unexpected_proposers = [b.message.proposer_index for b in new_signed_blocks if + b.message.proposer_index not in branch_tip.participants] + assert len(unexpected_proposers) == 0, \ + "Unexpected proposer: " + str(unexpected_proposers[0]) + + # Attesters are aligned with the partition + for b in new_signed_blocks: + attesters = _attesters_in_block(spec, branch_tips[sm_link].beacon_state, b) + unexpected_attesters = attesters.difference(branch_tip.participants) + assert len(unexpected_attesters) == 0, \ + "Unexpected attester: " + str(unexpected_attesters.pop()) + ", slot " + str(b.message.slot) + # Store the result branch_tips[sm_link] = new_branch_tip signed_blocks = signed_blocks + new_signed_blocks @@ -407,15 +449,16 @@ def _generate_blocks(spec, anchor_state, sm_links, rnd: random.Random, debug) -> @with_presets([MINIMAL], reason="too slow") def test_generated_sm_links(spec, state): """ - Creates a tree of supermajority links + Generates a tree of supermajority links """ - input = [(2, 3), (2, 4), (3, 8), (3, 7), (0, 2)] + input = [(2, 3), (2, 4), (3, 8), (3, 7)] seed = 1 debug = False + anchor_epoch = 2 rnd = random.Random(seed) sm_links = [SmLink(l) for l in input] - signed_blocks = _generate_blocks(spec, state, sm_links, rnd, debug) + signed_blocks = _generate_blocks(spec, state, sm_links, anchor_epoch, rnd, debug) test_steps = [] # Store initialization From 5885bf40e483b8985a65f2af78c359e477ff6f52 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Tue, 19 Dec 2023 21:19:56 +0600 Subject: [PATCH 006/111] Switch sm_links generator to minizinc model --- .../fork_choice/gecode_config/gecode.msc | 16 +++ .../fork_choice/minizinc/Block_cover.mzn | 39 +++++++ .../fork_choice/minizinc/Block_predicates.mzn | 45 ++++++++ .../fork_choice/minizinc/Block_tree.mzn | 32 ++++++ .../phase0/fork_choice/minizinc/Chain.mzn | 100 ++++++++++++++++++ .../fork_choice/minizinc/Epoch_Transition.mzn | 49 +++++++++ .../phase0/fork_choice/minizinc/SM_links.mzn | 19 ++++ .../test/phase0/fork_choice/test_generated.py | 43 +++++++- .../fork_choice_generated/requirements.txt | 1 + 9 files changed, 339 insertions(+), 5 deletions(-) create mode 100644 tests/core/pyspec/eth2spec/test/phase0/fork_choice/gecode_config/gecode.msc create mode 100644 tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/Block_cover.mzn create mode 100644 tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/Block_predicates.mzn create mode 100644 tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/Block_tree.mzn create mode 100644 tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/Chain.mzn create mode 100644 tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/Epoch_Transition.mzn create mode 100644 tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/SM_links.mzn diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/gecode_config/gecode.msc b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/gecode_config/gecode.msc new file mode 100644 index 0000000000..7ef0fa7fd3 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/gecode_config/gecode.msc @@ -0,0 +1,16 @@ +{ + "id": "org.gecode.gecode", + "name": "Gecode", + "description": "Gecode FlatZinc executable", + "version": "6.2.0", + "mznlib": "/usr/local/share/gecode", + "executable": "/usr/local/bin/fzn-gecode", + "tags": ["cp","int", "float", "set", "restart"], + "stdFlags": ["-a","-f","-n","-p","-r","-s","-t"], + "supportsMzn": false, + "supportsFzn": true, + "needsSolns2Out": true, + "needsMznExecutable": false, + "needsStdlibDir": false, + "isGUIApplication": false +} diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/Block_cover.mzn b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/Block_cover.mzn new file mode 100644 index 0000000000..9c3f2e0f9c --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/Block_cover.mzn @@ -0,0 +1,39 @@ +include "globals.mzn"; + +int: NE = 4; +int: NS = NE * 2; +int: NB = 5; + +type Block = record(var SLOT: slot, var BLOCK: parent, var EPOCH: bje, var EPOCH: buje); + +function var int: s2e(var int: e) = e div 2; +predicate valid_block(var BLOCK: b) = + let {var int: ep = s2e(slot[b]); var int: je = bje[b]; var int: uje = buje[b]} + in je <= uje /\ je <= ep /\ (ep != 0 -> je < ep) /\ uje <= ep; + +set of int: BLOCK = 0..(NB-1); +set of int: SLOT = 0..(NS-1); +set of int: EPOCH = 0..(NE-1); +array[BLOCK] of var BLOCK: parent; +array[BLOCK] of var SLOT: slot; +array[BLOCK] of var EPOCH: bje; +array[BLOCK] of var EPOCH: buje; + +var int: sje; +var int: suje; + + + +constraint maximum(sje, bje); +constraint maximum(suje, buje); + +constraint parent[0] == 0 /\ slot[0] == 0 /\ bje[0] == 0 /\ buje[0] == 0; +constraint forall(i in BLOCK)(if i == 0 then parent[i] == i else parent[i] != i endif); +constraint strictly_increasing(slot); +constraint forall(i in BLOCK)(valid_block(i)); +constraint buje[NB-1] > 2; + +constraint pred1 <-> is_leaf(block); +constraint pred2 <-> is_anscestor_of_sje(block); + +solve satisfy; diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/Block_predicates.mzn b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/Block_predicates.mzn new file mode 100644 index 0000000000..1cb0703004 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/Block_predicates.mzn @@ -0,0 +1,45 @@ +include "globals.mzn"; + +int: NoE = 4; +int: maxE = NoE - 1; +set of int: EPOCH = 0..maxE; + +type BS = record(EPOCH: e, EPOCH: je, EPOCH: uje); + + +var BS: block; +var EPOCH: curr_e; +var EPOCH: store_je; + +function var EPOCH: get_vse(var BS: block) = + if block.e < curr_e then block.uje else block.je endif; + +predicate valid_block_epochs(var BS: block) = + block.e <= curr_e /\ + block.je <= block.uje /\ (block.e != 0 -> block.je < block.e) /\ block.uje <= block.e; + +predicate valid_store_je() = + let { var BS: best_block; constraint valid_block_epochs(best_block); } + in get_vse(best_block) = store_je; + + +constraint valid_store_je(); +constraint get_vse(block) <= store_je; +constraint valid_block_epochs(block); + + +bool: store_je_eq_zero = false; +bool: block_vse_eq_store_je = false; +bool: prev_e_justified = false; +bool: block_uje_ge_store_je = false; +bool: block_vse_plus_two_ge_curr_e = false; +%bool: block_is_leaf; +%bool: block_descendant_of_store_just_root; +%bool: block_descendant_of_store_fin_chkpt; +%bool: block_fe_eq_zero = false; + +constraint store_je_eq_zero <-> store_je == 0; +constraint block_vse_eq_store_je <-> get_vse(block) == store_je; +constraint prev_e_justified <-> store_je == curr_e - 1; +constraint block_uje_ge_store_je <-> block.uje >= store_je; +constraint block_vse_plus_two_ge_curr_e <-> get_vse(block) + 2 >= curr_e; diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/Block_tree.mzn b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/Block_tree.mzn new file mode 100644 index 0000000000..09bea558d5 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/Block_tree.mzn @@ -0,0 +1,32 @@ +include "globals.mzn"; + +set of int: BLOCKS = 0..7; + +array[BLOCKS] of var BLOCKS: parent; +array[BLOCKS] of var set of BLOCKS: children; +array[BLOCKS] of var int: depths; +array[BLOCKS] of var int: widths; + +function var int: count_children(var BLOCKS: block) = + sum(ch in BLOCKS)(if ch != 0 /\ parent[ch] == block then 1 else 0 endif); + +function var int: sub_tree_size(var BLOCKS: block) = + if count_children(block) == 0 then + 1 + else + sum(ch in BLOCKS)(if ch != 0 /\ parent[ch] == block then sub_tree_size(ch) else 0 endif) + endif; + + +array[BLOCKS] of var int: tst; +array[BLOCKS] of var int: tst2; + +constraint forall(b in BLOCKS)(tst[b] == count_children(b)); +constraint forall(b in BLOCKS)(tst2[b] == card(children[b])); +constraint int_set_channel(parent, children); +constraint forall(b in BLOCKS)(if b == 0 then depths[b] == 0 else depths[b] == depths[parent[b]] + 1 endif); + +constraint forall(b in BLOCKS)(if b != 0 then parent[b] < b else parent[b] == b endif); +constraint forall(b in BLOCKS)(count_children(b) <= 2); +constraint forall(b in BLOCKS)(depths[b] <= 5); +constraint forall(b in BLOCKS)(widths[b] == sub_tree_size(b)); \ No newline at end of file diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/Chain.mzn b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/Chain.mzn new file mode 100644 index 0000000000..54c4e17d15 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/Chain.mzn @@ -0,0 +1,100 @@ +include "globals.mzn"; + +int: NoE = 5; +int: maxE = NoE - 1; +set of int: EPOCH = 0..maxE; + +type SML = record(EPOCH: source, EPOCH: target); +type BS = record(EPOCH: e, EPOCH: je, EPOCH: uje); + + +var BS: block; +var EPOCH: curr_e; +var EPOCH: store_je; + +function var EPOCH: get_vse(var BS: block) = + if block.e < curr_e then block.uje else block.je endif; + +predicate valid_block_epochs(var BS: block) = + block.e <= curr_e /\ + block.je <= block.uje /\ (block.e != 0 -> block.je < block.e) /\ block.uje <= block.e; + +predicate valid_store_je() = + let { var BS: best_block; constraint valid_block_epochs(best_block); } + in get_vse(best_block) = store_je; + + +type BSt = record(EPOCH: e, EPOCH: pje, EPOCH: cje); + +predicate ok(var opt int: v) = + not absent(v); + +predicate ok_sml(var opt SML: sml) = + ok(sml.source) /\ ok(sml.target); + +predicate correct_sml(var opt SML: sml) = + ok(sml.source) <-> ok(sml.target); + +predicate valid_link(var opt SML: sml, var EPOCH: src, var EPOCH: tgt) = + if ok_sml(sml) then + sml.source == src /\ sml.target == tgt + endif + ; + +function var BSt: epoch_trans(var BSt: block, var opt SML: p_sml, var opt SML: c_sml) = + let { + var EPOCH: new_pje = block.cje; + var EPOCH: new_cje = + if ok_sml(c_sml) /\ valid_link(c_sml, block.cje, block.e) then + block.e + else + if ok_sml(p_sml) /\ valid_link(p_sml, block.pje, block.e-1) then + block.e - 1 + else + block.cje + endif + endif; + } in (e: block.e + 1, pje: new_pje, cje: new_cje); + + +constraint valid_store_je(); +constraint get_vse(block) <= store_je; +constraint valid_block_epochs(block); +%constraint block.e >= 2 /\ block.je >= 1; +%constraint block.je < block.uje /\ block.e < curr_e; + + +opt bool: store_je_eq_zero = true; +opt bool: block_vse_eq_store_je = <>; +opt bool: prev_e_justified = <>; +opt bool: block_uje_ge_store_je = <>; +opt bool: block_vse_plus_two_ge_curr_e = <>; +%bool: block_is_leaf; +%bool: block_descendant_of_store_just_root; +%bool: block_descendant_of_store_fin_chkpt; +%bool: block_fe_eq_zero = false; + +constraint store_je_eq_zero <-> (store_je == 0); +constraint block_vse_eq_store_je <-> (get_vse(block) == store_je); +constraint prev_e_justified <-> (store_je == curr_e - 1); +constraint block_uje_ge_store_je <-> (block.uje >= store_je); +constraint block_vse_plus_two_ge_curr_e <-> (get_vse(block) + 2 >= curr_e); + + +predicate epoch_trans_p(var BSt: b, var opt SML: sml, var BSt: r) = + r.e == b.e + 1 + /\ r.pje = b.cje + /\ if ok_sml(sml) then + (r.cje = b.e /\ sml.source = b.cje /\ sml.target = b.e) + \/ (r.cje = b.e - 1 /\ sml.source = b.pje /\ sml.target = b.e-1) + else + r.cje = b.cje + endif; + +function var bool: etclo(array[0..maxE] of var BSt: sts, int: sz) = + sts[0] = (e: 0, pje: 0, cje: 0) + /\ forall(i in 0..sz-1) (epoch_trans_p(sts[i], smls[i], sts[i+1])); + +array[0..maxE] of var BSt: sts; +array[0..maxE-1] of var opt SML: smls; +constraint etclo(sts, 2); diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/Epoch_Transition.mzn b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/Epoch_Transition.mzn new file mode 100644 index 0000000000..d6390522a0 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/Epoch_Transition.mzn @@ -0,0 +1,49 @@ +include "globals.mzn"; + +int: NoE = 5; +int: maxE = NoE-1; +set of int: EPOCH = 0..maxE; +int: NoL = 3; +set of int: LINKS = 0..NoL-1; + + +array[0..maxE] of var EPOCH: epochs; +array[0..maxE] of var EPOCH: pjes; +array[0..maxE] of var EPOCH: cjes; + +array[LINKS] of var EPOCH: sources; +array[LINKS] of var EPOCH: targets; + + +predicate ex_link(var EPOCH: src, var EPOCH: tgt) = + exists(i in LINKS)(sources[i] == src /\ targets[i] == tgt); + + +predicate trans(0..maxE: from, 0..maxE: to) = + let { + var EPOCH: e = epochs[from]; var EPOCH: cje = cjes[from]; var EPOCH: pje = pjes[from]; + var EPOCH: ne = epochs[to]; var EPOCH: ncje = cjes[to]; var EPOCH: npje = pjes[to]; + var bool: curr_link = ex_link(cje, e); var bool: prev_link = (e > 0) -> ex_link(pje, e-1); + } in + ((curr_link /\ ncje == e) + \/ (prev_link /\ ncje == e-1) + \/ (ncje == cje)) + /\ npje == cje /\ ne == e + 1 +; + + +predicate surround_vote(var LINKS: a, var LINKS: b) = + sources[a] < sources[b] /\ targets[b] < targets[a]; + + +constraint forall(i in LINKS)(sources[i] < targets[i]); +constraint forall(i in LINKS)(sources[i] == 0 \/ member(targets, sources[i])); +constraint strictly_increasing(targets); +constraint forall(i,j in LINKS where i != j)(not surround_vote(i,j)); + +constraint epochs[0] == 0; +constraint pjes[0] == 0; +constraint cjes[0] == 0; +constraint forall(i in 0..maxE-1)(trans(i, i+1)); + + diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/SM_links.mzn b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/SM_links.mzn new file mode 100644 index 0000000000..b8690239a9 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/SM_links.mzn @@ -0,0 +1,19 @@ +include "globals.mzn"; + +int: AE; % anchor epoch +int: NE; % num of epochs +int: NL; % num of super-majority links + +set of int: EPOCH = AE..(AE+NE-1); +set of int: LINKS = 0..(NL-1); + +array[LINKS] of var EPOCH: sources; +array[LINKS] of var EPOCH: targets; + +predicate surround_vote(var LINKS: a, var LINKS: b) = + sources[a] < sources[b] /\ targets[b] < targets[a]; + +constraint forall(i in LINKS)(sources[i] < targets[i]); +constraint forall(i in LINKS)(sources[i] == AE \/ member(targets, sources[i])); +constraint strictly_increasing(targets); +constraint forall(i,j in LINKS where i != j)(not surround_vote(i,j)); diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_generated.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_generated.py index 5233051448..3474c49e1d 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_generated.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_generated.py @@ -1,4 +1,10 @@ import random +from pathlib import Path +from minizinc import ( + Instance, + Model, + Solver, +) from eth2spec.test.context import ( spec_state_test, with_altair_and_later, @@ -320,7 +326,7 @@ def _print_epoch(spec, epoch_state, signed_blocks): return ret -def _generate_blocks(spec, genesis_state, sm_links, anchor_epoch, rnd: random.Random, debug) -> []: +def _generate_blocks(spec, genesis_state, sm_links, rnd: random.Random, debug) -> []: """ Generates a sequence of blocks satisfying a tree of supermajority links specified in the sm_links list, i.e. a sequence of blocks with attestations required to create given supermajority links. @@ -340,12 +346,17 @@ def _generate_blocks(spec, genesis_state, sm_links, anchor_epoch, rnd: random.Ra :return: Sequence of signed blocks oredered by a slot number. """ + assert any(sm_links) + state = genesis_state.copy() signed_blocks = [] + # Find anchor epoch + anchor_epoch = min(sm_links, key=lambda l: l.source).source + genesis_tip = BranchTip(state.copy(), [], [*range(0, len(state.validators))]) - # Move the state to the anchor_epoch + # Advance the state to the anchor_epoch anchor_tip = genesis_tip for epoch in (spec.GENESIS_EPOCH, anchor_epoch): new_signed_blocks, anchor_tip = _advance_branch_to_next_epoch(spec, anchor_tip, epoch) @@ -451,14 +462,36 @@ def test_generated_sm_links(spec, state): """ Generates a tree of supermajority links """ - input = [(2, 3), (2, 4), (3, 8), (3, 7)] + iminput = [(2, 3), (2, 4), (3, 8), (3, 7)] seed = 1 debug = False anchor_epoch = 2 + number_of_epochs = 5 + number_of_links = 4 + sm_links_model_path = '/Users/n0ble/workspace/eth2.0-specs/tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/SM_links.mzn' + + # Dependencies: + # 1. Install minizinc binary + # https://www.minizinc.org/doc-2.5.5/en/installation_detailed_linux.html + # 2. Install minizinc python lib + # pip install minizinc + # 3. Install and confifure gecode solver: + # https://www.minizinc.org/doc-2.5.5/en/installation_detailed_linux.html#gecode + sm_links = Model(sm_links_model_path) + solver = Solver.lookup("gecode") + instance = Instance(solver, sm_links) + instance['AE'] = anchor_epoch # anchor epoch + instance['NE'] = number_of_epochs # number of epochs, starting from AE + instance['NL'] = number_of_links # number of super-majority links + + solutions = instance.solve(all_solutions=True) + res = [] + for i in range(len(solutions)): + res.append({'sources': solutions[i, 'sources'], 'targets': solutions[i, 'targets'] }) rnd = random.Random(seed) - sm_links = [SmLink(l) for l in input] - signed_blocks = _generate_blocks(spec, state, sm_links, anchor_epoch, rnd, debug) + sm_links = [SmLink(l) for l in zip(res[0]['sources'], res[0]['targets'])] + signed_blocks = _generate_blocks(spec, state, sm_links, rnd, debug) test_steps = [] # Store initialization diff --git a/tests/generators/fork_choice_generated/requirements.txt b/tests/generators/fork_choice_generated/requirements.txt index 1822486863..d41c75d07a 100644 --- a/tests/generators/fork_choice_generated/requirements.txt +++ b/tests/generators/fork_choice_generated/requirements.txt @@ -1,2 +1,3 @@ pytest>=4.4 +minizinc>=0.9.0 ../../../[generator] From 0c7ff17c747fff24326ce1c4088837f41efe09ae Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 21 Dec 2023 17:11:37 +0600 Subject: [PATCH 007/111] Rename sm_links test and its deps --- .../{ => model}/gecode_config/gecode.msc | 0 .../{ => model}/minizinc/Block_cover.mzn | 0 .../{ => model}/minizinc/Block_predicates.mzn | 0 .../{ => model}/minizinc/Block_tree.mzn | 0 .../{ => model}/minizinc/Chain.mzn | 0 .../{ => model}/minizinc/Epoch_Transition.mzn | 0 .../{ => model}/minizinc/SM_links.mzn | 0 ...nerated.py => test_sm_links_tree_model.py} | 21 +++++++++---------- .../generators/fork_choice_generated/main.py | 3 +-- 9 files changed, 11 insertions(+), 13 deletions(-) rename tests/core/pyspec/eth2spec/test/phase0/fork_choice/{ => model}/gecode_config/gecode.msc (100%) rename tests/core/pyspec/eth2spec/test/phase0/fork_choice/{ => model}/minizinc/Block_cover.mzn (100%) rename tests/core/pyspec/eth2spec/test/phase0/fork_choice/{ => model}/minizinc/Block_predicates.mzn (100%) rename tests/core/pyspec/eth2spec/test/phase0/fork_choice/{ => model}/minizinc/Block_tree.mzn (100%) rename tests/core/pyspec/eth2spec/test/phase0/fork_choice/{ => model}/minizinc/Chain.mzn (100%) rename tests/core/pyspec/eth2spec/test/phase0/fork_choice/{ => model}/minizinc/Epoch_Transition.mzn (100%) rename tests/core/pyspec/eth2spec/test/phase0/fork_choice/{ => model}/minizinc/SM_links.mzn (100%) rename tests/core/pyspec/eth2spec/test/phase0/fork_choice/{test_generated.py => test_sm_links_tree_model.py} (96%) diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/gecode_config/gecode.msc b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/model/gecode_config/gecode.msc similarity index 100% rename from tests/core/pyspec/eth2spec/test/phase0/fork_choice/gecode_config/gecode.msc rename to tests/core/pyspec/eth2spec/test/phase0/fork_choice/model/gecode_config/gecode.msc diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/Block_cover.mzn b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/model/minizinc/Block_cover.mzn similarity index 100% rename from tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/Block_cover.mzn rename to tests/core/pyspec/eth2spec/test/phase0/fork_choice/model/minizinc/Block_cover.mzn diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/Block_predicates.mzn b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/model/minizinc/Block_predicates.mzn similarity index 100% rename from tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/Block_predicates.mzn rename to tests/core/pyspec/eth2spec/test/phase0/fork_choice/model/minizinc/Block_predicates.mzn diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/Block_tree.mzn b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/model/minizinc/Block_tree.mzn similarity index 100% rename from tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/Block_tree.mzn rename to tests/core/pyspec/eth2spec/test/phase0/fork_choice/model/minizinc/Block_tree.mzn diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/Chain.mzn b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/model/minizinc/Chain.mzn similarity index 100% rename from tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/Chain.mzn rename to tests/core/pyspec/eth2spec/test/phase0/fork_choice/model/minizinc/Chain.mzn diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/Epoch_Transition.mzn b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/model/minizinc/Epoch_Transition.mzn similarity index 100% rename from tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/Epoch_Transition.mzn rename to tests/core/pyspec/eth2spec/test/phase0/fork_choice/model/minizinc/Epoch_Transition.mzn diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/SM_links.mzn b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/model/minizinc/SM_links.mzn similarity index 100% rename from tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/SM_links.mzn rename to tests/core/pyspec/eth2spec/test/phase0/fork_choice/model/minizinc/SM_links.mzn diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_generated.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py similarity index 96% rename from tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_generated.py rename to tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py index 3474c49e1d..b9fb4e791f 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_generated.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py @@ -458,17 +458,17 @@ def _generate_blocks(spec, genesis_state, sm_links, rnd: random.Random, debug) - @with_altair_and_later @spec_state_test @with_presets([MINIMAL], reason="too slow") -def test_generated_sm_links(spec, state): +def test_sm_links_tree_model(spec, state): """ Generates a tree of supermajority links """ - iminput = [(2, 3), (2, 4), (3, 8), (3, 7)] seed = 1 - debug = False + debug = True anchor_epoch = 2 number_of_epochs = 5 number_of_links = 4 - sm_links_model_path = '/Users/n0ble/workspace/eth2.0-specs/tests/core/pyspec/eth2spec/test/phase0/fork_choice/minizinc/SM_links.mzn' + sm_links_model_path = './model/minizinc/SM_links.mzn' + sm_links_solution_index = 0 # Dependencies: # 1. Install minizinc binary @@ -480,17 +480,16 @@ def test_generated_sm_links(spec, state): sm_links = Model(sm_links_model_path) solver = Solver.lookup("gecode") instance = Instance(solver, sm_links) - instance['AE'] = anchor_epoch # anchor epoch - instance['NE'] = number_of_epochs # number of epochs, starting from AE - instance['NL'] = number_of_links # number of super-majority links + instance['AE'] = anchor_epoch # anchor epoch + instance['NE'] = number_of_epochs # number of epochs, starting from AE + instance['NL'] = number_of_links # number of super-majority links solutions = instance.solve(all_solutions=True) - res = [] - for i in range(len(solutions)): - res.append({'sources': solutions[i, 'sources'], 'targets': solutions[i, 'targets'] }) rnd = random.Random(seed) - sm_links = [SmLink(l) for l in zip(res[0]['sources'], res[0]['targets'])] + sources = solutions[sm_links_solution_index, 'sources'] + targets = solutions[sm_links_solution_index, 'targets'] + sm_links = [SmLink(l) for l in zip(sources, targets)] signed_blocks = _generate_blocks(spec, state, sm_links, rnd, debug) test_steps = [] diff --git a/tests/generators/fork_choice_generated/main.py b/tests/generators/fork_choice_generated/main.py index 59120fa9c5..24ce65a126 100644 --- a/tests/generators/fork_choice_generated/main.py +++ b/tests/generators/fork_choice_generated/main.py @@ -1,10 +1,9 @@ from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods from eth2spec.test.helpers.constants import ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110 - if __name__ == "__main__": generated_modes = {key: 'eth2spec.test.phase0.fork_choice.test_' + key for key in [ - 'generated', + 'sm_links_tree_model', ]} fork_choice_compliance_testing_modes = { From a6e9dd579773bc6e3a0892970a1fa74a30643159 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 21 Dec 2023 17:15:14 +0600 Subject: [PATCH 008/111] Disable debug mode --- .../test/phase0/fork_choice/test_sm_links_tree_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py index b9fb4e791f..9004868751 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py @@ -463,7 +463,7 @@ def test_sm_links_tree_model(spec, state): Generates a tree of supermajority links """ seed = 1 - debug = True + debug = False anchor_epoch = 2 number_of_epochs = 5 number_of_links = 4 From e00346ae9719a363f10b1aa774c36617ea0e6265 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 21 Dec 2023 20:56:13 +0600 Subject: [PATCH 009/111] Fixes and polishing --- .../fork_choice/test_sm_links_tree_model.py | 55 +++++++++++++------ 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py index 9004868751..eb9a998fff 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py @@ -307,14 +307,15 @@ def _print_block(spec, epoch_state, signed_block): attesters = _attesters_in_block(spec, epoch_state, signed_block) attester_str = str(attesters) if any(attesters) else "{}" - return "b(p: " + str(block.proposer_index) + ", a:" + attester_str + ")" + return 'b(r=' + str(spec.hash_tree_root(block))[:6] + ', p=' + str( + block.proposer_index) + ', a=' + attester_str + ')' def _print_epoch(spec, epoch_state, signed_blocks): epoch = spec.get_current_epoch(epoch_state) start_slot = spec.compute_start_slot_at_epoch(epoch) ret = "" - for slot in range(start_slot, start_slot + spec.SLOTS_PER_EPOCH + 1): + for slot in range(start_slot, start_slot + spec.SLOTS_PER_EPOCH): blocks_in_slot = [b for b in signed_blocks if b.message.slot == slot] if ret != "": ret = ret + " <- " @@ -347,20 +348,35 @@ def _generate_blocks(spec, genesis_state, sm_links, rnd: random.Random, debug) - :return: Sequence of signed blocks oredered by a slot number. """ assert any(sm_links) + # Cases where source == GENESIS_EPOCH + 1 aren't supported, + # because the protocol can't justify in epoch GENESIS_EPOCH + 1 + assert len([sm_link for sm_link in sm_links if sm_link.source == spec.GENESIS_EPOCH + 1]) == 0 + + if debug: + print('\nsm links to satisfy:', sm_links) state = genesis_state.copy() signed_blocks = [] - # Find anchor epoch + # Find anchor epoch and check it anchor_epoch = min(sm_links, key=lambda l: l.source).source genesis_tip = BranchTip(state.copy(), [], [*range(0, len(state.validators))]) # Advance the state to the anchor_epoch anchor_tip = genesis_tip - for epoch in (spec.GENESIS_EPOCH, anchor_epoch): + for epoch in range(spec.GENESIS_EPOCH, anchor_epoch + 1): + pre_state = anchor_tip.beacon_state new_signed_blocks, anchor_tip = _advance_branch_to_next_epoch(spec, anchor_tip, epoch) signed_blocks = signed_blocks + new_signed_blocks + if debug: + post_state = anchor_tip.beacon_state + print('\nepoch', str(epoch) + ':') + print('branch(*, *):', _print_epoch(spec, pre_state, new_signed_blocks)) + print(' ', len(anchor_tip.participants), 'participants:', anchor_tip.participants) + print(' ', 'state.current_justified_checkpoint:', + '(epoch=' + str(post_state.current_justified_checkpoint.epoch) + + ', root=' + str(post_state.current_justified_checkpoint.root)[:6] + ')') # branch_tips hold the most recent state, validator partition and not included attestations for every fork # Initialize branch tips with the anchor tip @@ -388,7 +404,7 @@ def _generate_blocks(spec, genesis_state, sm_links, rnd: random.Random, debug) - # Debug checks if debug: - print("\nepoch ", current_epoch, ":") + print('\nepoch', str(current_epoch) + ':') # Partitions are disjoint for l1 in current_epoch_sm_links: l1_participants = branch_tips[l1].participants @@ -397,27 +413,27 @@ def _generate_blocks(spec, genesis_state, sm_links, rnd: random.Random, debug) - l2_participants = branch_tips[l2].participants intersection = set(l1_participants).intersection(l2_participants) assert len(intersection) == 0, \ - str(l1) + " and " + str(l2) + " has common participants: " + str(intersection) + str(l1) + ' and ' + str(l2) + ' has common participants: ' + str(intersection) # Advance every branch taking into account attestations from past epochs and voting partitions for sm_link in current_epoch_sm_links: branch_tip = branch_tips[sm_link] assert spec.get_current_epoch(branch_tip.beacon_state) == current_epoch, \ - "Unexpected current_epoch(branch_tip.beacon_state)" + 'Unexpected current_epoch(branch_tip.beacon_state)' new_signed_blocks, new_branch_tip = _advance_branch_to_next_epoch(spec, branch_tip, current_epoch) # Run sanity checks post_state = new_branch_tip.beacon_state assert spec.get_current_epoch(post_state) == current_epoch + 1, \ - "Unexpected post_state epoch" + 'Unexpected post_state epoch' if sm_link.target == current_epoch: assert post_state.previous_justified_checkpoint.epoch == sm_link.source, \ - "Unexpected previous_justified_checkpoint.epoch" + 'Unexpected previous_justified_checkpoint.epoch' assert post_state.current_justified_checkpoint.epoch == sm_link.target, \ - "Unexpected current_justified_checkpoint.epoch" + 'Unexpected current_justified_checkpoint.epoch' else: assert post_state.current_justified_checkpoint.epoch == sm_link.source, \ - "Unexpected current_justified_checkpoint.epoch" + 'Unexpected current_justified_checkpoint.epoch' # Create the first block of the next epoch to realize justification on chain, # neccessary only when the fork won't be advanced in any of the future epochs @@ -428,9 +444,13 @@ def _generate_blocks(spec, genesis_state, sm_links, rnd: random.Random, debug) - # Debug output if debug: - print(sm_link, ":", _print_epoch(spec, branch_tips[sm_link].beacon_state, new_signed_blocks)) - print(" ", len(branch_tips[sm_link].participants), "participants:", + print('branch' + str(sm_link) + ':', + _print_epoch(spec, branch_tips[sm_link].beacon_state, new_signed_blocks)) + print(' ', len(branch_tips[sm_link].participants), 'participants:', branch_tips[sm_link].participants) + print(' ', 'state.current_justified_checkpoint:', + '(epoch=' + str(post_state.current_justified_checkpoint.epoch) + + ', root=' + str(post_state.current_justified_checkpoint.root)[:6] + ')') # Debug checks if debug: @@ -438,14 +458,14 @@ def _generate_blocks(spec, genesis_state, sm_links, rnd: random.Random, debug) - unexpected_proposers = [b.message.proposer_index for b in new_signed_blocks if b.message.proposer_index not in branch_tip.participants] assert len(unexpected_proposers) == 0, \ - "Unexpected proposer: " + str(unexpected_proposers[0]) + 'Unexpected proposer: ' + str(unexpected_proposers[0]) # Attesters are aligned with the partition for b in new_signed_blocks: attesters = _attesters_in_block(spec, branch_tips[sm_link].beacon_state, b) unexpected_attesters = attesters.difference(branch_tip.participants) assert len(unexpected_attesters) == 0, \ - "Unexpected attester: " + str(unexpected_attesters.pop()) + ", slot " + str(b.message.slot) + 'Unexpected attester: ' + str(unexpected_attesters.pop()) + ', slot ' + str(b.message.slot) # Store the result branch_tips[sm_link] = new_branch_tip @@ -463,11 +483,11 @@ def test_sm_links_tree_model(spec, state): Generates a tree of supermajority links """ seed = 1 - debug = False + debug = True anchor_epoch = 2 number_of_epochs = 5 number_of_links = 4 - sm_links_model_path = './model/minizinc/SM_links.mzn' + sm_links_model_path = '../../core/pyspec/eth2spec/test/phase0/fork_choice/model/minizinc/SM_links.mzn' sm_links_solution_index = 0 # Dependencies: @@ -490,6 +510,7 @@ def test_sm_links_tree_model(spec, state): sources = solutions[sm_links_solution_index, 'sources'] targets = solutions[sm_links_solution_index, 'targets'] sm_links = [SmLink(l) for l in zip(sources, targets)] + signed_blocks = _generate_blocks(spec, state, sm_links, rnd, debug) test_steps = [] From cf385510728a22a8688d256921750b18a2e703c1 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 28 Dec 2023 18:08:07 +0600 Subject: [PATCH 010/111] Update sm_links test and generator --- .../fork_choice/test_sm_links_tree_model.py | 209 ++++++++++-------- .../generators/fork_choice_generated/main.py | 116 ++++++++-- .../model/gecode_config/gecode.msc | 0 .../model/minizinc/Block_cover.mzn | 0 .../model/minizinc/Block_predicates.mzn | 0 .../model/minizinc/Block_tree.mzn | 0 .../model/minizinc/Chain.mzn | 0 .../model/minizinc/Epoch_Transition.mzn | 0 .../model/minizinc/SM_links.mzn | 0 9 files changed, 223 insertions(+), 102 deletions(-) rename tests/{core/pyspec/eth2spec/test/phase0/fork_choice => generators/fork_choice_generated}/model/gecode_config/gecode.msc (100%) rename tests/{core/pyspec/eth2spec/test/phase0/fork_choice => generators/fork_choice_generated}/model/minizinc/Block_cover.mzn (100%) rename tests/{core/pyspec/eth2spec/test/phase0/fork_choice => generators/fork_choice_generated}/model/minizinc/Block_predicates.mzn (100%) rename tests/{core/pyspec/eth2spec/test/phase0/fork_choice => generators/fork_choice_generated}/model/minizinc/Block_tree.mzn (100%) rename tests/{core/pyspec/eth2spec/test/phase0/fork_choice => generators/fork_choice_generated}/model/minizinc/Chain.mzn (100%) rename tests/{core/pyspec/eth2spec/test/phase0/fork_choice => generators/fork_choice_generated}/model/minizinc/Epoch_Transition.mzn (100%) rename tests/{core/pyspec/eth2spec/test/phase0/fork_choice => generators/fork_choice_generated}/model/minizinc/SM_links.mzn (100%) diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py index eb9a998fff..d93614f3d8 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py @@ -29,6 +29,7 @@ state_transition_and_sign_block, transition_to, next_slot, + next_epoch, ) from eth2spec.test.helpers.block import ( build_empty_block_for_next_slot, @@ -54,10 +55,11 @@ def target(self): class BranchTip: - def __init__(self, beacon_state, attestations, participants): + def __init__(self, beacon_state, attestations, participants, eventually_justified_checkpoint): self.beacon_state = beacon_state self.attestations = attestations self.participants = participants + self.eventually_justified_checkpoint = eventually_justified_checkpoint def _justifying_participation_rate(rnd: random.Random): @@ -79,13 +81,13 @@ def _create_new_branch_tip(spec, branch_tips: dict[SmLink:BranchTip], sm_link: S # Find all forks with justified source tips_with_justified_source = [s for s in branch_tips.values() - if s.beacon_state.current_justified_checkpoint.epoch == - sm_link.source] + if s.eventually_justified_checkpoint.epoch == sm_link.source] assert len(tips_with_justified_source) > 0 # Find and return the most adanced one most_recent_tip = max(tips_with_justified_source, key=lambda s: s.beacon_state.slot) - return BranchTip(most_recent_tip.beacon_state.copy(), most_recent_tip.attestations.copy(), []) + return BranchTip(most_recent_tip.beacon_state.copy(), most_recent_tip.attestations.copy(), [], + most_recent_tip.eventually_justified_checkpoint) def _sample_validator_partition(spec, state, epoch, participation_rate, rnd) -> []: @@ -231,7 +233,7 @@ def _attest_to_slot(spec, state, slot_to_attest, participants_filter): return attestations_in_slot -def _advance_branch_to_next_epoch(spec, branch_tip, current_epoch): +def _advance_branch_to_next_epoch(spec, branch_tip, enable_attesting=True): """ Advances a state of the block tree branch to the next epoch respecting validators participanting in building and attesting to this branch. @@ -246,6 +248,7 @@ def participants_filter(comm): signed_blocks = [] attestations = branch_tip.attestations.copy() state = branch_tip.beacon_state.copy() + current_epoch = spec.get_current_epoch(state) target_slot = spec.compute_start_slot_at_epoch(current_epoch + 1) while state.slot < target_slot: @@ -255,19 +258,33 @@ def participants_filter(comm): signed_block, state, attestations = _produce_block(spec, state, attestations) signed_blocks.append(signed_block) - # Produce attestations - attestations_in_slot = _attest_to_slot(spec, state, state.slot, participants_filter) - # And prepend them to the list - attestations = list(attestations_in_slot) + attestations + if enable_attesting: + # Produce attestations + attestations_in_slot = _attest_to_slot(spec, state, state.slot, participants_filter) + # And prepend them to the list + attestations = list(attestations_in_slot) + attestations # Advance a slot next_slot(spec, state) # Cleanup attestations by removing outdated ones attestations = [a for a in attestations if - a.data.target in (spec.get_previous_epoch(state), spec.get_current_epoch(state))] + a.data.target.epoch in (spec.get_previous_epoch(state), spec.get_current_epoch(state))] - return signed_blocks, BranchTip(state, attestations, branch_tip.participants) + eventually_justified_checkpoint = state.current_justified_checkpoint + + # If not all attestations are included on chain + # and attestation.data.target.epoch > beacon_state.current_justified_checkpoint.epoch + # compute eventually_justified_checkpoint, a would be state.current_justified_checkpoint if all attestations + # were included; this computation respects the validator partition that was building the branch + if len(attestations) > 0 \ + and attestations[0].data.target.epoch > state.current_justified_checkpoint.epoch \ + and attestations[0].data.target.epoch > spec.GENESIS_EPOCH: + branch_tip = BranchTip(state, attestations, branch_tip.participants, state.current_justified_checkpoint) + _, new_branch_tip = _advance_branch_to_next_epoch(spec, branch_tip, enable_attesting=False) + eventually_justified_checkpoint = new_branch_tip.beacon_state.current_justified_checkpoint + + return signed_blocks, BranchTip(state, attestations, branch_tip.participants, eventually_justified_checkpoint) def _any_change_to_validator_partitions(spec, sm_links, current_epoch, anchor_epoch) -> bool: @@ -294,21 +311,28 @@ def _any_change_to_validator_partitions(spec, sm_links, current_epoch, anchor_ep return False -def _attesters_in_block(spec, epoch_state, signed_block): +def _attesters_in_block(spec, epoch_state, signed_block, target_epoch): block = signed_block.message attesters = set() for a in block.body.attestations: - attesters.update(spec.get_attesting_indices(epoch_state, a.data, a.aggregation_bits)) + if a.data.target.epoch == target_epoch: + attesters.update(spec.get_attesting_indices(epoch_state, a.data, a.aggregation_bits)) return attesters def _print_block(spec, epoch_state, signed_block): block = signed_block.message - attesters = _attesters_in_block(spec, epoch_state, signed_block) - attester_str = str(attesters) if any(attesters) else "{}" + if spec.get_current_epoch(epoch_state) > spec.GENESIS_EPOCH: + prev_attesters = _attesters_in_block(spec, epoch_state, signed_block, spec.get_previous_epoch(epoch_state)) + else: + prev_attesters = set() + + curr_attesters = _attesters_in_block(spec, epoch_state, signed_block, spec.get_current_epoch(epoch_state)) + prev_attester_str = 'a_prev=' + str(prev_attesters) if any(prev_attesters) else 'a_prev={}' + curr_attester_str = 'a_curr=' + str(curr_attesters) if any(curr_attesters) else 'a_curr={}' return 'b(r=' + str(spec.hash_tree_root(block))[:6] + ', p=' + str( - block.proposer_index) + ', a=' + attester_str + ')' + block.proposer_index) + ', ' + prev_attester_str + ', ' + curr_attester_str + ')' def _print_epoch(spec, epoch_state, signed_blocks): @@ -352,22 +376,20 @@ def _generate_blocks(spec, genesis_state, sm_links, rnd: random.Random, debug) - # because the protocol can't justify in epoch GENESIS_EPOCH + 1 assert len([sm_link for sm_link in sm_links if sm_link.source == spec.GENESIS_EPOCH + 1]) == 0 - if debug: - print('\nsm links to satisfy:', sm_links) - state = genesis_state.copy() signed_blocks = [] # Find anchor epoch and check it anchor_epoch = min(sm_links, key=lambda l: l.source).source - genesis_tip = BranchTip(state.copy(), [], [*range(0, len(state.validators))]) + genesis_tip = BranchTip(state.copy(), [], [*range(0, len(state.validators))], + state.current_justified_checkpoint) # Advance the state to the anchor_epoch anchor_tip = genesis_tip for epoch in range(spec.GENESIS_EPOCH, anchor_epoch + 1): pre_state = anchor_tip.beacon_state - new_signed_blocks, anchor_tip = _advance_branch_to_next_epoch(spec, anchor_tip, epoch) + new_signed_blocks, anchor_tip = _advance_branch_to_next_epoch(spec, anchor_tip) signed_blocks = signed_blocks + new_signed_blocks if debug: post_state = anchor_tip.beacon_state @@ -377,6 +399,9 @@ def _generate_blocks(spec, genesis_state, sm_links, rnd: random.Random, debug) - print(' ', 'state.current_justified_checkpoint:', '(epoch=' + str(post_state.current_justified_checkpoint.epoch) + ', root=' + str(post_state.current_justified_checkpoint.root)[:6] + ')') + print(' ', 'eventually_justified_checkpoint:', + '(epoch=' + str(anchor_tip.eventually_justified_checkpoint.epoch) + + ', root=' + str(anchor_tip.eventually_justified_checkpoint.root)[:6] + ')') # branch_tips hold the most recent state, validator partition and not included attestations for every fork # Initialize branch tips with the anchor tip @@ -399,7 +424,8 @@ def _generate_blocks(spec, genesis_state, sm_links, rnd: random.Random, debug) - partitions = _compute_validator_partitions(spec, branch_tips, current_epoch_sm_links, current_epoch, rnd) for l in partitions.keys(): old_tip_state = branch_tips[l] - new_tip_state = BranchTip(old_tip_state.beacon_state, old_tip_state.attestations, partitions[l]) + new_tip_state = BranchTip(old_tip_state.beacon_state, old_tip_state.attestations, partitions[l], + old_tip_state.eventually_justified_checkpoint) branch_tips[l] = new_tip_state # Debug checks @@ -420,7 +446,7 @@ def _generate_blocks(spec, genesis_state, sm_links, rnd: random.Random, debug) - branch_tip = branch_tips[sm_link] assert spec.get_current_epoch(branch_tip.beacon_state) == current_epoch, \ 'Unexpected current_epoch(branch_tip.beacon_state)' - new_signed_blocks, new_branch_tip = _advance_branch_to_next_epoch(spec, branch_tip, current_epoch) + new_signed_blocks, new_branch_tip = _advance_branch_to_next_epoch(spec, branch_tip) # Run sanity checks post_state = new_branch_tip.beacon_state @@ -429,47 +455,71 @@ def _generate_blocks(spec, genesis_state, sm_links, rnd: random.Random, debug) - if sm_link.target == current_epoch: assert post_state.previous_justified_checkpoint.epoch == sm_link.source, \ 'Unexpected previous_justified_checkpoint.epoch' - assert post_state.current_justified_checkpoint.epoch == sm_link.target, \ - 'Unexpected current_justified_checkpoint.epoch' + assert new_branch_tip.eventually_justified_checkpoint.epoch == sm_link.target, \ + 'Unexpected eventually_justified_checkpoint.epoch' else: assert post_state.current_justified_checkpoint.epoch == sm_link.source, \ 'Unexpected current_justified_checkpoint.epoch' - # Create the first block of the next epoch to realize justification on chain, - # neccessary only when the fork won't be advanced in any of the future epochs + # If the fork won't be advanced in the future epochs + # ensure 1) all yet not included attestations are included on chain by advancing it to epoch N+1 + # 2) justification is realized by advancing it to epoch N+2 is_fork_advanced_in_future = any((l for l in sm_links if l.source == sm_link.target)) if sm_link.target == current_epoch and not is_fork_advanced_in_future: - tip_block, _, _ = _produce_block(spec, new_branch_tip.beacon_state, new_branch_tip.attestations) + # Advance to epoch N+1 + advanced_signed_blocks, advanced_branch_tip = _advance_branch_to_next_epoch(spec, new_branch_tip, + enable_attesting=False) + new_signed_blocks = new_signed_blocks + advanced_signed_blocks + + # Find a slot for which a proposer is among participants + state = advanced_branch_tip.beacon_state.copy() + while (spec.get_beacon_proposer_index(state) not in advanced_branch_tip.participants): + next_slot(spec, state) + + # Build a block in epoch > N+1 + tip_block, _, _ = _produce_block(spec, state, []) new_signed_blocks.append(tip_block) - # Debug output - if debug: - print('branch' + str(sm_link) + ':', - _print_epoch(spec, branch_tips[sm_link].beacon_state, new_signed_blocks)) - print(' ', len(branch_tips[sm_link].participants), 'participants:', - branch_tips[sm_link].participants) - print(' ', 'state.current_justified_checkpoint:', - '(epoch=' + str(post_state.current_justified_checkpoint.epoch) + - ', root=' + str(post_state.current_justified_checkpoint.root)[:6] + ')') - - # Debug checks - if debug: - # Proposers are aligned with the partition - unexpected_proposers = [b.message.proposer_index for b in new_signed_blocks if - b.message.proposer_index not in branch_tip.participants] - assert len(unexpected_proposers) == 0, \ - 'Unexpected proposer: ' + str(unexpected_proposers[0]) - - # Attesters are aligned with the partition - for b in new_signed_blocks: - attesters = _attesters_in_block(spec, branch_tips[sm_link].beacon_state, b) - unexpected_attesters = attesters.difference(branch_tip.participants) - assert len(unexpected_attesters) == 0, \ - 'Unexpected attester: ' + str(unexpected_attesters.pop()) + ', slot ' + str(b.message.slot) - - # Store the result - branch_tips[sm_link] = new_branch_tip - signed_blocks = signed_blocks + new_signed_blocks + assert state.current_justified_checkpoint.epoch == sm_link.target, \ + 'Unexpected state.current_justified_checkpoint' + + # Debug output + if debug: + print('branch' + str(sm_link) + ':', + _print_epoch(spec, branch_tips[sm_link].beacon_state, new_signed_blocks)) + print(' ', len(branch_tips[sm_link].participants), 'participants:', + new_branch_tip.participants) + print(' ', 'state.current_justified_checkpoint:', + '(epoch=' + str(post_state.current_justified_checkpoint.epoch) + + ', root=' + str(post_state.current_justified_checkpoint.root)[:6] + ')') + print(' ', 'eventually_justified_checkpoint:', + '(epoch=' + str(new_branch_tip.eventually_justified_checkpoint.epoch) + + ', root=' + str(new_branch_tip.eventually_justified_checkpoint.root)[:6] + ')') + + # Debug checks + if debug: + # Proposers are aligned with the partition + unexpected_proposers = [b.message.proposer_index for b in new_signed_blocks if + b.message.proposer_index not in branch_tip.participants] + assert len(unexpected_proposers) == 0, \ + 'Unexpected proposer: ' + str(unexpected_proposers[0]) + + # Attesters are aligned with the partition + current_epoch_state = branch_tips[sm_link].beacon_state + for b in new_signed_blocks: + # Attesting indexes from on chain attestations + attesters = _attesters_in_block(spec, current_epoch_state, b, current_epoch) + # Attesting indexes from not yet included attestations + for a in new_branch_tip.attestations: + if a.data.target.epoch == current_epoch: + attesters.update(spec.get_attesting_indices(current_epoch_state, a.data, a.aggregation_bits)) + unexpected_attesters = attesters.difference(branch_tip.participants) + assert len(unexpected_attesters) == 0, \ + 'Unexpected attester: ' + str(unexpected_attesters.pop()) + ', slot ' + str(b.message.slot) + + # Store the result + branch_tips[sm_link] = new_branch_tip + signed_blocks = signed_blocks + new_signed_blocks # Sort blocks by a slot return sorted(signed_blocks, key=lambda b: b.message.slot) @@ -477,42 +527,25 @@ def _generate_blocks(spec, genesis_state, sm_links, rnd: random.Random, debug) - @with_altair_and_later @spec_state_test -@with_presets([MINIMAL], reason="too slow") -def test_sm_links_tree_model(spec, state): - """ - Generates a tree of supermajority links - """ - seed = 1 - debug = True - anchor_epoch = 2 - number_of_epochs = 5 - number_of_links = 4 - sm_links_model_path = '../../core/pyspec/eth2spec/test/phase0/fork_choice/model/minizinc/SM_links.mzn' - sm_links_solution_index = 0 - - # Dependencies: - # 1. Install minizinc binary - # https://www.minizinc.org/doc-2.5.5/en/installation_detailed_linux.html - # 2. Install minizinc python lib - # pip install minizinc - # 3. Install and confifure gecode solver: - # https://www.minizinc.org/doc-2.5.5/en/installation_detailed_linux.html#gecode - sm_links = Model(sm_links_model_path) - solver = Solver.lookup("gecode") - instance = Instance(solver, sm_links) - instance['AE'] = anchor_epoch # anchor epoch - instance['NE'] = number_of_epochs # number of epochs, starting from AE - instance['NL'] = number_of_links # number of super-majority links - - solutions = instance.solve(all_solutions=True) +def test_sm_links_tree_model(spec, state, debug=False, seed=1, sm_links=None): + # This test is mainly used for the test generation purposes + # Thus seed and sm_links are provided by the generator + # Define sm_links and seed explicitly to execute a certain run of this test + if sm_links is None: + return - rnd = random.Random(seed) - sources = solutions[sm_links_solution_index, 'sources'] - targets = solutions[sm_links_solution_index, 'targets'] - sm_links = [SmLink(l) for l in zip(sources, targets)] + if debug: + print('\nseed:', seed) + print('\nsm_links:', sm_links) + sm_links = [SmLink(l) for l in sm_links] + rnd = random.Random(seed) signed_blocks = _generate_blocks(spec, state, sm_links, rnd, debug) + # Yield run parameters + yield 'seed', 'meta', seed + yield 'sm_links', 'meta', str(sm_links) + test_steps = [] # Store initialization store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) diff --git a/tests/generators/fork_choice_generated/main.py b/tests/generators/fork_choice_generated/main.py index 24ce65a126..d6d0b5a679 100644 --- a/tests/generators/fork_choice_generated/main.py +++ b/tests/generators/fork_choice_generated/main.py @@ -1,16 +1,104 @@ -from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators, combine_mods -from eth2spec.test.helpers.constants import ALTAIR, BELLATRIX, CAPELLA, DENEB, EIP6110 +from eth2spec.test.helpers.constants import ALTAIR +from eth2spec.gen_helpers.gen_base import gen_runner +from eth2spec.test.helpers.constants import MINIMAL +from eth2spec.gen_helpers.gen_base.gen_typing import TestCase, TestProvider +from typing import Iterable +from importlib import import_module +from eth2spec.utils import bls +from eth2spec.test.helpers.typing import SpecForkName, PresetBaseName +from minizinc import Instance, Model, Solver +import random + +BLS_ACTIVE = False + + +def _import_test_fn(): + src = import_module('eth2spec.test.phase0.fork_choice.test_sm_links_tree_model') + print("generating test vectors from tests source: %s" % src.__name__) + return getattr(src, 'test_sm_links_tree_model') + + +def _find_sm_link_solutions(anchor_epoch: int, + number_of_epochs: int, + number_of_links: int) -> Iterable[Iterable[tuple]]: + # Dependencies: + # 1. Install minizinc binary + # https://www.minizinc.org/doc-2.5.5/en/installation_detailed_linux.html + # 2. Install minizinc python lib + # pip install minizinc + # 3. Install and confifure gecode solver: + # https://www.minizinc.org/doc-2.5.5/en/installation_detailed_linux.html#gecode + sm_links = Model('./model/minizinc/SM_links.mzn') + solver = Solver.lookup("gecode") + instance = Instance(solver, sm_links) + instance['AE'] = anchor_epoch # anchor epoch + instance['NE'] = number_of_epochs # number of epochs, starting from AE + instance['NL'] = number_of_links # number of super-majority links + + solutions = instance.solve(all_solutions=True) + for i in range(len(solutions)): + yield [_ for _ in zip(solutions[i, 'sources'], solutions[i, 'targets'])] + + +def _create_providers(forks: Iterable[SpecForkName], + presets: Iterable[PresetBaseName], + debug: bool, + initial_seed: int, + anchor_epoch: int, + number_of_epochs: int, + number_of_links: int, + number_of_variations: int) -> Iterable[TestProvider]: + def prepare_fn() -> None: + bls.use_milagro() + return + + def make_cases_fn() -> Iterable[TestCase]: + test_fn = _import_test_fn() + solutions = _find_sm_link_solutions(anchor_epoch, number_of_epochs, number_of_links) + + seeds = [initial_seed] + if number_of_variations > 1: + rnd = random.Random(initial_seed) + seeds = [rnd.randint(1, 10000) for _ in range(number_of_variations)] + seeds[0] = initial_seed + + for i, solution in enumerate(solutions): + for seed in seeds: + for fork_name in forks: + for preset_name in presets: + yield TestCase(fork_name=fork_name, + preset_name=preset_name, + runner_name='fork_choice_generated', + handler_name='sm_links_tree_model', + suite_name='fork_choice', + case_name='sm_links_tree_model_' + str(i) + '_' + str(seed), + case_fn=lambda: test_fn(generator_mode=True, + phase=fork_name, + preset=preset_name, + bls_active=BLS_ACTIVE, + debug=debug, + seed=seed, + sm_links=solution)) + + yield TestProvider(prepare=prepare_fn, make_cases=make_cases_fn) + if __name__ == "__main__": - generated_modes = {key: 'eth2spec.test.phase0.fork_choice.test_' + key for key in [ - 'sm_links_tree_model', - ]} - - fork_choice_compliance_testing_modes = { - ALTAIR: generated_modes, - BELLATRIX: generated_modes, - CAPELLA: generated_modes, - DENEB: generated_modes - } - - run_state_test_generators(runner_name="fork_choice_generated", all_mods=fork_choice_compliance_testing_modes) + anchor_epoch = 2 + number_of_epochs = 2 + number_of_links = 1 + debug = True + initial_seed = 9326 + number_of_variations = 1 + + forks = [ALTAIR] + presets = [MINIMAL] + + gen_runner.run_generator('fork_choice_generated', _create_providers(forks=forks, + presets=presets, + debug=debug, + initial_seed=initial_seed, + anchor_epoch=anchor_epoch, + number_of_epochs=number_of_epochs, + number_of_links=number_of_links, + number_of_variations=number_of_variations)) diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/model/gecode_config/gecode.msc b/tests/generators/fork_choice_generated/model/gecode_config/gecode.msc similarity index 100% rename from tests/core/pyspec/eth2spec/test/phase0/fork_choice/model/gecode_config/gecode.msc rename to tests/generators/fork_choice_generated/model/gecode_config/gecode.msc diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/model/minizinc/Block_cover.mzn b/tests/generators/fork_choice_generated/model/minizinc/Block_cover.mzn similarity index 100% rename from tests/core/pyspec/eth2spec/test/phase0/fork_choice/model/minizinc/Block_cover.mzn rename to tests/generators/fork_choice_generated/model/minizinc/Block_cover.mzn diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/model/minizinc/Block_predicates.mzn b/tests/generators/fork_choice_generated/model/minizinc/Block_predicates.mzn similarity index 100% rename from tests/core/pyspec/eth2spec/test/phase0/fork_choice/model/minizinc/Block_predicates.mzn rename to tests/generators/fork_choice_generated/model/minizinc/Block_predicates.mzn diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/model/minizinc/Block_tree.mzn b/tests/generators/fork_choice_generated/model/minizinc/Block_tree.mzn similarity index 100% rename from tests/core/pyspec/eth2spec/test/phase0/fork_choice/model/minizinc/Block_tree.mzn rename to tests/generators/fork_choice_generated/model/minizinc/Block_tree.mzn diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/model/minizinc/Chain.mzn b/tests/generators/fork_choice_generated/model/minizinc/Chain.mzn similarity index 100% rename from tests/core/pyspec/eth2spec/test/phase0/fork_choice/model/minizinc/Chain.mzn rename to tests/generators/fork_choice_generated/model/minizinc/Chain.mzn diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/model/minizinc/Epoch_Transition.mzn b/tests/generators/fork_choice_generated/model/minizinc/Epoch_Transition.mzn similarity index 100% rename from tests/core/pyspec/eth2spec/test/phase0/fork_choice/model/minizinc/Epoch_Transition.mzn rename to tests/generators/fork_choice_generated/model/minizinc/Epoch_Transition.mzn diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/model/minizinc/SM_links.mzn b/tests/generators/fork_choice_generated/model/minizinc/SM_links.mzn similarity index 100% rename from tests/core/pyspec/eth2spec/test/phase0/fork_choice/model/minizinc/SM_links.mzn rename to tests/generators/fork_choice_generated/model/minizinc/SM_links.mzn From ae322c71c6799c871038fc0b783f2dfdf831789e Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 28 Dec 2023 18:46:30 +0600 Subject: [PATCH 011/111] Fix contraint reachability problem --- .../fork_choice/test_sm_links_tree_model.py | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py index d93614f3d8..888537c2c6 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py @@ -417,7 +417,13 @@ def _generate_blocks(spec, genesis_state, sm_links, rnd: random.Random, debug) - # Initialize new forks for l in (l for l in current_epoch_sm_links if branch_tips.get(l) is None): - branch_tips[l] = _create_new_branch_tip(spec, branch_tips, l) + new_branch_tip = _create_new_branch_tip(spec, branch_tips, l) + # Abort the test if any sm_links constraint appears to be unreachable + # because the justification of the source checkpoint hasn't been realized on chain yet + if l.target == current_epoch and new_branch_tip.beacon_state.current_justified_checkpoint.epoch < l.source: + return [] + + branch_tips[l] = new_branch_tip # Reshuffle partitions if needed if _any_change_to_validator_partitions(spec, sm_links, current_epoch, anchor_epoch): @@ -534,13 +540,26 @@ def test_sm_links_tree_model(spec, state, debug=False, seed=1, sm_links=None): if sm_links is None: return - if debug: - print('\nseed:', seed) - print('\nsm_links:', sm_links) + assert (1, 2) not in sm_links, '(1, 2) sm link is not supported due to unreachability' sm_links = [SmLink(l) for l in sm_links] - rnd = random.Random(seed) - signed_blocks = _generate_blocks(spec, state, sm_links, rnd, debug) + + # Find a reachable solution trying with different seeds if needed + # sm_links constraints may not have a solution beacause of the randomization affecting validator partitions + signed_blocks = [] + while True: + if debug: + print('\nseed:', seed) + print('\nsm_links:', sm_links) + + rnd = random.Random(seed) + signed_blocks = _generate_blocks(spec, state, sm_links, rnd, debug) + if len(signed_blocks) > 0: + break + + print('\nUnreachable constraints: sm_links: ' + str(sm_links) + ', seed=' + str(seed)) + + seed = rnd.randint(1, 10000) # Yield run parameters yield 'seed', 'meta', seed From 3a2d5fef0240dae235b2ddcb85fed627fc5f9aac Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 28 Dec 2023 18:52:10 +0600 Subject: [PATCH 012/111] Update debug output --- .../eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py index 888537c2c6..e45711cb10 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py @@ -560,6 +560,7 @@ def test_sm_links_tree_model(spec, state, debug=False, seed=1, sm_links=None): print('\nUnreachable constraints: sm_links: ' + str(sm_links) + ', seed=' + str(seed)) seed = rnd.randint(1, 10000) + print('Trying a different seed: ' + str(seed)) # Yield run parameters yield 'seed', 'meta', seed From 995cab2956fb098ef46dd434aa412208b3dc723a Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Fri, 29 Dec 2023 15:20:05 +0600 Subject: [PATCH 013/111] Add CLI args to fork choice generator --- .../gen_helpers/gen_base/gen_runner.py | 29 ++++--- .../fork_choice/test_sm_links_tree_model.py | 42 +++++----- .../generators/fork_choice_generated/main.py | 81 +++++++++++++++---- .../model/minizinc/SM_links.mzn | 3 + 4 files changed, 110 insertions(+), 45 deletions(-) diff --git a/tests/core/pyspec/eth2spec/gen_helpers/gen_base/gen_runner.py b/tests/core/pyspec/eth2spec/gen_helpers/gen_base/gen_runner.py index 3ab2e9eea8..e7fdd48fb8 100644 --- a/tests/core/pyspec/eth2spec/gen_helpers/gen_base/gen_runner.py +++ b/tests/core/pyspec/eth2spec/gen_helpers/gen_base/gen_runner.py @@ -140,17 +140,7 @@ def should_skip_case_dir(case_dir, is_force, diagnostics_obj): return is_skip, diagnostics_obj -def run_generator(generator_name, test_providers: Iterable[TestProvider]): - """ - Implementation for a general test generator. - :param generator_name: The name of the generator. (lowercase snake_case) - :param test_providers: A list of test provider, - each of these returns a callable that returns an iterable of test cases. - The call to get the iterable may set global configuration, - and the iterable should not be resumed after a pause with a change of that configuration. - :return: - """ - +def create_arg_parser(generator_name: str) -> argparse.ArgumentParser: parser = argparse.ArgumentParser( prog="gen-" + generator_name, description=f"Generate YAML test suite files for {generator_name}", @@ -194,6 +184,23 @@ def run_generator(generator_name, test_providers: Iterable[TestProvider]): help="if set only print tests to generate, do not actually run the test and dump the target data", ) + return parser + + +def run_generator(generator_name, test_providers: Iterable[TestProvider], parser: argparse.ArgumentParser = None): + """ + Implementation for a general test generator. + :param generator_name: The name of the generator. (lowercase snake_case) + :param test_providers: A list of test provider, + each of these returns a callable that returns an iterable of test cases. + The call to get the iterable may set global configuration, + and the iterable should not be resumed after a pause with a change of that configuration. + :return: + """ + + if parser is None: + parser = create_arg_parser(generator_name) + args = parser.parse_args() output_dir = args.output_dir if not args.force: diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py index e45711cb10..8fe934a5b2 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py @@ -233,6 +233,22 @@ def _attest_to_slot(spec, state, slot_to_attest, participants_filter): return attestations_in_slot +def _compute_eventually_justified_epoch(spec, state, attestations, participants): + # If not all attestations are included on chain + # and attestation.data.target.epoch > beacon_state.current_justified_checkpoint.epoch + # compute eventually_justified_checkpoint, a would be state.current_justified_checkpoint if all attestations + # were included; this computation respects the validator partition that was building the branch + if len(attestations) > 0 \ + and attestations[0].data.target.epoch > state.current_justified_checkpoint.epoch \ + and attestations[0].data.target.epoch > spec.GENESIS_EPOCH: + branch_tip = BranchTip(state, attestations, participants, state.current_justified_checkpoint) + _, new_branch_tip = _advance_branch_to_next_epoch(spec, branch_tip, enable_attesting=False) + + return new_branch_tip.beacon_state.current_justified_checkpoint + else: + return state.current_justified_checkpoint + + def _advance_branch_to_next_epoch(spec, branch_tip, enable_attesting=True): """ Advances a state of the block tree branch to the next epoch @@ -271,18 +287,8 @@ def participants_filter(comm): attestations = [a for a in attestations if a.data.target.epoch in (spec.get_previous_epoch(state), spec.get_current_epoch(state))] - eventually_justified_checkpoint = state.current_justified_checkpoint - - # If not all attestations are included on chain - # and attestation.data.target.epoch > beacon_state.current_justified_checkpoint.epoch - # compute eventually_justified_checkpoint, a would be state.current_justified_checkpoint if all attestations - # were included; this computation respects the validator partition that was building the branch - if len(attestations) > 0 \ - and attestations[0].data.target.epoch > state.current_justified_checkpoint.epoch \ - and attestations[0].data.target.epoch > spec.GENESIS_EPOCH: - branch_tip = BranchTip(state, attestations, branch_tip.participants, state.current_justified_checkpoint) - _, new_branch_tip = _advance_branch_to_next_epoch(spec, branch_tip, enable_attesting=False) - eventually_justified_checkpoint = new_branch_tip.beacon_state.current_justified_checkpoint + eventually_justified_checkpoint = _compute_eventually_justified_epoch(spec, state, attestations, + branch_tip.participants) return signed_blocks, BranchTip(state, attestations, branch_tip.participants, eventually_justified_checkpoint) @@ -540,7 +546,7 @@ def test_sm_links_tree_model(spec, state, debug=False, seed=1, sm_links=None): if sm_links is None: return - assert (1, 2) not in sm_links, '(1, 2) sm link is not supported due to unreachability' + assert (1, 2) not in sm_links, '(1, 2) sm link is not supported due to unsatisfiability' sm_links = [SmLink(l) for l in sm_links] @@ -556,11 +562,11 @@ def test_sm_links_tree_model(spec, state, debug=False, seed=1, sm_links=None): signed_blocks = _generate_blocks(spec, state, sm_links, rnd, debug) if len(signed_blocks) > 0: break - - print('\nUnreachable constraints: sm_links: ' + str(sm_links) + ', seed=' + str(seed)) - - seed = rnd.randint(1, 10000) - print('Trying a different seed: ' + str(seed)) + else: + new_seed = rnd.randint(1, 10000) + print('\nUnsatisfiable constraints: sm_links: ' + str(sm_links) + ', seed=' + str( + seed) + ', will retry with seed=' + str(new_seed)) + seed = new_seed # Yield run parameters yield 'seed', 'meta', seed diff --git a/tests/generators/fork_choice_generated/main.py b/tests/generators/fork_choice_generated/main.py index d6d0b5a679..f4fd4b66db 100644 --- a/tests/generators/fork_choice_generated/main.py +++ b/tests/generators/fork_choice_generated/main.py @@ -10,6 +10,7 @@ import random BLS_ACTIVE = False +GENERATOR_NAME = 'fork_choice_generated' def _import_test_fn(): @@ -68,7 +69,7 @@ def make_cases_fn() -> Iterable[TestCase]: for preset_name in presets: yield TestCase(fork_name=fork_name, preset_name=preset_name, - runner_name='fork_choice_generated', + runner_name=GENERATOR_NAME, handler_name='sm_links_tree_model', suite_name='fork_choice', case_name='sm_links_tree_model_' + str(i) + '_' + str(seed), @@ -84,21 +85,69 @@ def make_cases_fn() -> Iterable[TestCase]: if __name__ == "__main__": - anchor_epoch = 2 - number_of_epochs = 2 - number_of_links = 1 - debug = True - initial_seed = 9326 - number_of_variations = 1 - forks = [ALTAIR] presets = [MINIMAL] - gen_runner.run_generator('fork_choice_generated', _create_providers(forks=forks, - presets=presets, - debug=debug, - initial_seed=initial_seed, - anchor_epoch=anchor_epoch, - number_of_epochs=number_of_epochs, - number_of_links=number_of_links, - number_of_variations=number_of_variations)) + arg_parser = gen_runner.create_arg_parser(GENERATOR_NAME) + + arg_parser.add_argument( + '--fc-gen-debug', + dest='fc_gen_debug', + action='store_true', + default=False, + required=False, + help='If set provides debug output and enable additional checks for generated chains', + ) + arg_parser.add_argument( + '--fc-gen-seed', + dest='fc_gen_seed', + default=1, + type=int, + required=False, + help='Provides randomizer with initial seed' + ) + arg_parser.add_argument( + '--fc-gen-variations', + dest='fc_gen_variations', + default=1, + type=int, + required=False, + help='Number of random variations per each solution' + ) + arg_parser.add_argument( + '--fc-gen-anchor-epoch', + dest='fc_gen_anchor_epoch', + default=0, + type=int, + required=False, + help='Anchor epoch' + ) + arg_parser.add_argument( + '--fc-gen-epochs', + dest='fc_gen_epochs', + default=2, + type=int, + required=False, + help='Number of epochs beyond the anchor epoch' + ) + arg_parser.add_argument( + '--fc-gen-links', + dest='fc_gen_links', + default=1, + type=int, + required=False, + help='Number of super majority links per solution' + ) + + args = arg_parser.parse_args() + + gen_runner.run_generator(GENERATOR_NAME, + _create_providers(forks=forks, + presets=presets, + debug=args.fc_gen_debug, + initial_seed=args.fc_gen_seed, + anchor_epoch=args.fc_gen_anchor_epoch, + number_of_epochs=args.fc_gen_epochs, + number_of_links=args.fc_gen_links, + number_of_variations=args.fc_gen_variations), + arg_parser) diff --git a/tests/generators/fork_choice_generated/model/minizinc/SM_links.mzn b/tests/generators/fork_choice_generated/model/minizinc/SM_links.mzn index b8690239a9..93b6cfba78 100644 --- a/tests/generators/fork_choice_generated/model/minizinc/SM_links.mzn +++ b/tests/generators/fork_choice_generated/model/minizinc/SM_links.mzn @@ -17,3 +17,6 @@ constraint forall(i in LINKS)(sources[i] < targets[i]); constraint forall(i in LINKS)(sources[i] == AE \/ member(targets, sources[i])); constraint strictly_increasing(targets); constraint forall(i,j in LINKS where i != j)(not surround_vote(i,j)); + +% Exclude (1, 2) SM link which is unreachable for the Gasper protocol +constraint forall(i in LINKS)(not (sources[i] == 1 /\ targets[i] == 2)); From beac058ab1d0ebfd0644c1cbfbe9b38a239f1ac1 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Tue, 13 Feb 2024 13:13:10 +0600 Subject: [PATCH 014/111] Fix bugs, inrease verbosity --- .../fork_choice/test_sm_links_tree_model.py | 100 ++++++++++-------- 1 file changed, 53 insertions(+), 47 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py index 8fe934a5b2..b0b28a64eb 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py @@ -40,7 +40,7 @@ MAX_JUSTIFICATION_RATE = 99 MIN_JUSTIFICATION_RATE = 91 -MAX_UNDERJUSTIFICATION_RATE = 70 +MAX_UNDERJUSTIFICATION_RATE = 66 MIN_UNDERJUSTIFICATION_RATE = 55 @@ -457,21 +457,26 @@ def _generate_blocks(spec, genesis_state, sm_links, rnd: random.Random, debug) - for sm_link in current_epoch_sm_links: branch_tip = branch_tips[sm_link] assert spec.get_current_epoch(branch_tip.beacon_state) == current_epoch, \ - 'Unexpected current_epoch(branch_tip.beacon_state)' + 'Unexpected current_epoch(branch_tip.beacon_state): ' + str( + spec.get_current_epoch(branch_tip.beacon_state)) + ' != ' + str(current_epoch) new_signed_blocks, new_branch_tip = _advance_branch_to_next_epoch(spec, branch_tip) # Run sanity checks post_state = new_branch_tip.beacon_state assert spec.get_current_epoch(post_state) == current_epoch + 1, \ - 'Unexpected post_state epoch' + 'Unexpected post_state epoch: ' + str(spec.get_current_epoch(post_state)) + ' != ' + str( + current_epoch + 1) if sm_link.target == current_epoch: assert post_state.previous_justified_checkpoint.epoch == sm_link.source, \ - 'Unexpected previous_justified_checkpoint.epoch' + 'Unexpected previous_justified_checkpoint.epoch: ' + str( + post_state.previous_justified_checkpoint.epoch) + ' != ' + str(sm_link.source) assert new_branch_tip.eventually_justified_checkpoint.epoch == sm_link.target, \ - 'Unexpected eventually_justified_checkpoint.epoch' - else: - assert post_state.current_justified_checkpoint.epoch == sm_link.source, \ - 'Unexpected current_justified_checkpoint.epoch' + 'Unexpected eventually_justified_checkpoint.epoch: ' + str( + new_branch_tip.eventually_justified_checkpoint.epoch) + ' != ' + str(sm_link.target) + elif (sm_link.source != new_branch_tip.eventually_justified_checkpoint.epoch): + # Abort the test as the justification of the source checkpoint can't be realized on chain + # because of the lack of the block space + return [] # If the fork won't be advanced in the future epochs # ensure 1) all yet not included attestations are included on chain by advancing it to epoch N+1 @@ -493,45 +498,46 @@ def _generate_blocks(spec, genesis_state, sm_links, rnd: random.Random, debug) - new_signed_blocks.append(tip_block) assert state.current_justified_checkpoint.epoch == sm_link.target, \ - 'Unexpected state.current_justified_checkpoint' - - # Debug output - if debug: - print('branch' + str(sm_link) + ':', - _print_epoch(spec, branch_tips[sm_link].beacon_state, new_signed_blocks)) - print(' ', len(branch_tips[sm_link].participants), 'participants:', - new_branch_tip.participants) - print(' ', 'state.current_justified_checkpoint:', - '(epoch=' + str(post_state.current_justified_checkpoint.epoch) + - ', root=' + str(post_state.current_justified_checkpoint.root)[:6] + ')') - print(' ', 'eventually_justified_checkpoint:', - '(epoch=' + str(new_branch_tip.eventually_justified_checkpoint.epoch) + - ', root=' + str(new_branch_tip.eventually_justified_checkpoint.root)[:6] + ')') - - # Debug checks - if debug: - # Proposers are aligned with the partition - unexpected_proposers = [b.message.proposer_index for b in new_signed_blocks if - b.message.proposer_index not in branch_tip.participants] - assert len(unexpected_proposers) == 0, \ - 'Unexpected proposer: ' + str(unexpected_proposers[0]) - - # Attesters are aligned with the partition - current_epoch_state = branch_tips[sm_link].beacon_state - for b in new_signed_blocks: - # Attesting indexes from on chain attestations - attesters = _attesters_in_block(spec, current_epoch_state, b, current_epoch) - # Attesting indexes from not yet included attestations - for a in new_branch_tip.attestations: - if a.data.target.epoch == current_epoch: - attesters.update(spec.get_attesting_indices(current_epoch_state, a.data, a.aggregation_bits)) - unexpected_attesters = attesters.difference(branch_tip.participants) - assert len(unexpected_attesters) == 0, \ - 'Unexpected attester: ' + str(unexpected_attesters.pop()) + ', slot ' + str(b.message.slot) - - # Store the result - branch_tips[sm_link] = new_branch_tip - signed_blocks = signed_blocks + new_signed_blocks + 'Unexpected state.current_justified_checkpoint: ' + str( + state.current_justified_checkpoint.epoch) + ' != ' + str(sm_link.target) + + # Debug output + if debug: + print('branch' + str(sm_link) + ':', + _print_epoch(spec, branch_tips[sm_link].beacon_state, new_signed_blocks)) + print(' ', len(branch_tips[sm_link].participants), 'participants:', + new_branch_tip.participants) + print(' ', 'state.current_justified_checkpoint:', + '(epoch=' + str(post_state.current_justified_checkpoint.epoch) + + ', root=' + str(post_state.current_justified_checkpoint.root)[:6] + ')') + print(' ', 'eventually_justified_checkpoint:', + '(epoch=' + str(new_branch_tip.eventually_justified_checkpoint.epoch) + + ', root=' + str(new_branch_tip.eventually_justified_checkpoint.root)[:6] + ')') + + # Debug checks + if debug: + # Proposers are aligned with the partition + unexpected_proposers = [b.message.proposer_index for b in new_signed_blocks if + b.message.proposer_index not in branch_tip.participants] + assert len(unexpected_proposers) == 0, \ + 'Unexpected proposer: ' + str(unexpected_proposers[0]) + + # Attesters are aligned with the partition + current_epoch_state = branch_tips[sm_link].beacon_state + for b in new_signed_blocks: + # Attesting indexes from on chain attestations + attesters = _attesters_in_block(spec, current_epoch_state, b, current_epoch) + # Attesting indexes from not yet included attestations + for a in new_branch_tip.attestations: + if a.data.target.epoch == current_epoch: + attesters.update(spec.get_attesting_indices(current_epoch_state, a.data, a.aggregation_bits)) + unexpected_attesters = attesters.difference(branch_tip.participants) + assert len(unexpected_attesters) == 0, \ + 'Unexpected attester: ' + str(unexpected_attesters.pop()) + ', slot ' + str(b.message.slot) + + # Store the result + branch_tips[sm_link] = new_branch_tip + signed_blocks = signed_blocks + new_signed_blocks # Sort blocks by a slot return sorted(signed_blocks, key=lambda b: b.message.slot) From e07f5803f251a535510d0b33288d5f64be260c3d Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 15 Feb 2024 18:16:24 +0600 Subject: [PATCH 015/111] Add basic block tree model instantiator --- .../fork_choice/test_sm_links_tree_model.py | 108 +++++++++++++++++- 1 file changed, 102 insertions(+), 6 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py index b0b28a64eb..55fe62bc04 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py @@ -43,6 +43,8 @@ MAX_UNDERJUSTIFICATION_RATE = 66 MIN_UNDERJUSTIFICATION_RATE = 55 +EMPTY_SLOTS_RATE = 3 +MAX_TIPS_TO_ATTEST = 2 class SmLink(tuple): @property @@ -206,7 +208,7 @@ def _produce_block(spec, state, attestations): return signed_block, post_state, not_included_attestations -def _attest_to_slot(spec, state, slot_to_attest, participants_filter): +def _attest_to_slot(spec, state, slot_to_attest, participants_filter=None): """ Creates attestation is a slot respecting participating validators. :return: produced attestations @@ -218,7 +220,7 @@ def _attest_to_slot(spec, state, slot_to_attest, participants_filter): attestations_in_slot = [] for index in range(committees_per_slot): beacon_committee = spec.get_beacon_committee(state, slot_to_attest, index) - participants = participants_filter(beacon_committee) + participants = beacon_committee if participants_filter is None else participants_filter(beacon_committee) if any(participants): attestation = get_valid_attestation( spec, @@ -357,7 +359,19 @@ def _print_epoch(spec, epoch_state, signed_blocks): return ret -def _generate_blocks(spec, genesis_state, sm_links, rnd: random.Random, debug) -> []: +def _print_block_tree(spec, root_state, signed_blocks): + ret = "" + epoch_state = root_state.copy() + for block in signed_blocks: + transition_to(spec, epoch_state, block.message.slot) + if ret != "": + ret = ret + " <- " + ret = ret + "s(" + str(block.message.slot) + ", " + _print_block(spec, epoch_state, block) + ")" + + return ret + + +def _generate_blocks(spec, genesis_state, sm_links, rnd: random.Random, debug, block_parents) -> []: """ Generates a sequence of blocks satisfying a tree of supermajority links specified in the sm_links list, i.e. a sequence of blocks with attestations required to create given supermajority links. @@ -530,7 +544,8 @@ def _generate_blocks(spec, genesis_state, sm_links, rnd: random.Random, debug) - # Attesting indexes from not yet included attestations for a in new_branch_tip.attestations: if a.data.target.epoch == current_epoch: - attesters.update(spec.get_attesting_indices(current_epoch_state, a.data, a.aggregation_bits)) + attesters.update( + spec.get_attesting_indices(current_epoch_state, a.data, a.aggregation_bits)) unexpected_attesters = attesters.difference(branch_tip.participants) assert len(unexpected_attesters) == 0, \ 'Unexpected attester: ' + str(unexpected_attesters.pop()) + ', slot ' + str(b.message.slot) @@ -539,13 +554,91 @@ def _generate_blocks(spec, genesis_state, sm_links, rnd: random.Random, debug) - branch_tips[sm_link] = new_branch_tip signed_blocks = signed_blocks + new_signed_blocks + # Process block tree model + block_tree = [] + highest_target_tip = branch_tips[highest_target_sm_link] + + attestations = highest_target_tip.attestations.copy() + post_states = [highest_target_tip.beacon_state.copy()] + current_slot = highest_target_tip.beacon_state.slot + block_index = 1 + block_tree_tips = set([0]) + + while block_index < len(block_parents): + # Next slot + current_slot += 1 + + # Propose a block if slot shouldn't be empty + if rnd.randint(1, 100) > EMPTY_SLOTS_RATE: + # Advance parent state to the current slot + parent_index = block_parents[block_index] + parent_state = post_states[parent_index].copy() + transition_to(spec, parent_state, current_slot) + + # Filter out non-viable attestations + attestations = [a for a in attestations if parent_state.slot <= a.data.slot + spec.SLOTS_PER_EPOCH] + + # Produce block + proposer = spec.get_beacon_proposer_index(parent_state) + signed_block, post_state, attestations = _produce_block(spec, parent_state, attestations) + block_tree.append(signed_block) + post_states.append(post_state) + + # Update tips + block_tree_tips.discard(parent_index) + block_tree_tips.add(block_index) + + # Next block + block_index += 1 + + # Attest to randomly selected tips + def split_list(lst, n): + k, m = divmod(len(lst), n) + return [lst[i * k + min(i, m):(i + 1) * k + min(i + 1, m)] for i in range(n)] + + attesting_tips = rnd.sample(block_tree_tips, min(len(block_tree_tips), MAX_TIPS_TO_ATTEST)) + attesters = split_list([i for i in range(len(post_state.validators))], len(attesting_tips)) + for index, attesting_block_index in enumerate(attesting_tips): + # Advance the state to the current slot + attesting_state = post_states[attesting_block_index] + transition_to(spec, attesting_state, current_slot) + + # Attest to the block + attestations_in_slot = _attest_to_slot(spec, attesting_state, attesting_state.slot, + lambda comm: [i for i in comm if i in attesters[index]]) + # Prepend attestations to the list + attestations = list(attestations_in_slot) + attestations + + signed_blocks = signed_blocks + block_tree + + if debug: + print('\nblock_tree:') + print('blocks: ', _print_block_tree(spec, post_states[0], block_tree)) + print(' ', 'state.current_justified_checkpoint:', + '(epoch=' + str(post_states[len(post_states) - 1].current_justified_checkpoint.epoch) + + ', root=' + str(post_states[len(post_states) - 1].current_justified_checkpoint.root)[:6] + ')') + # Sort blocks by a slot return sorted(signed_blocks, key=lambda b: b.message.slot) +def _print_head(spec, store): + head = spec.get_head(store) + weight = spec.get_weight(store, head) + state = store.checkpoint_states[store.justified_checkpoint] + total_active_balance = spec.get_total_active_balance(state) + + return '(slot=' + str(store.blocks[head].slot) + ', root=' + str(head)[:6] + ', weight=' + str( + weight * 100 // total_active_balance) + '%)' + + @with_altair_and_later @spec_state_test -def test_sm_links_tree_model(spec, state, debug=False, seed=1, sm_links=None): +def test_sm_links_tree_model(spec, state, debug=False, seed=4, sm_links=None): + block_parents = [0, 0, 0, 2, 2, 1, 1, 6, 6, 5, 5, 4, 7, 4, 3, 3] + sm_links = [(2, 3), (3, 4), (3, 6), (4, 7)] + debug = True + # This test is mainly used for the test generation purposes # Thus seed and sm_links are provided by the generator # Define sm_links and seed explicitly to execute a certain run of this test @@ -565,7 +658,7 @@ def test_sm_links_tree_model(spec, state, debug=False, seed=1, sm_links=None): print('\nsm_links:', sm_links) rnd = random.Random(seed) - signed_blocks = _generate_blocks(spec, state, sm_links, rnd, debug) + signed_blocks = _generate_blocks(spec, state, sm_links, rnd, debug, block_parents) if len(signed_blocks) > 0: break else: @@ -594,4 +687,7 @@ def test_sm_links_tree_model(spec, state, debug=False, seed=1, sm_links=None): block_root = block.hash_tree_root() assert store.blocks[block_root] == block + if debug: + print('head: ' + _print_head(spec, store)) + yield 'steps', test_steps From aab9f4b0247c01af015553a1b7449acf4823bf73 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Tue, 27 Feb 2024 20:35:08 +0600 Subject: [PATCH 016/111] Minor fixes --- .../fork_choice/test_sm_links_tree_model.py | 107 ++++++++++-------- 1 file changed, 59 insertions(+), 48 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py index 55fe62bc04..347a31ecda 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py @@ -40,7 +40,7 @@ MAX_JUSTIFICATION_RATE = 99 MIN_JUSTIFICATION_RATE = 91 -MAX_UNDERJUSTIFICATION_RATE = 66 +MAX_UNDERJUSTIFICATION_RATE = 65 MIN_UNDERJUSTIFICATION_RATE = 55 EMPTY_SLOTS_RATE = 3 @@ -343,11 +343,11 @@ def _print_block(spec, epoch_state, signed_block): block.proposer_index) + ', ' + prev_attester_str + ', ' + curr_attester_str + ')' -def _print_epoch(spec, epoch_state, signed_blocks): - epoch = spec.get_current_epoch(epoch_state) - start_slot = spec.compute_start_slot_at_epoch(epoch) +def _print_slot_range(spec, root_state, signed_blocks, start_slot, end_slot): ret = "" - for slot in range(start_slot, start_slot + spec.SLOTS_PER_EPOCH): + epoch_state = root_state.copy() + for slot in range(start_slot, end_slot): + transition_to(spec, epoch_state, slot) blocks_in_slot = [b for b in signed_blocks if b.message.slot == slot] if ret != "": ret = ret + " <- " @@ -359,19 +359,19 @@ def _print_epoch(spec, epoch_state, signed_blocks): return ret -def _print_block_tree(spec, root_state, signed_blocks): - ret = "" - epoch_state = root_state.copy() - for block in signed_blocks: - transition_to(spec, epoch_state, block.message.slot) - if ret != "": - ret = ret + " <- " - ret = ret + "s(" + str(block.message.slot) + ", " + _print_block(spec, epoch_state, block) + ")" +def _print_epoch(spec, epoch_state, signed_blocks): + epoch = spec.get_current_epoch(epoch_state) + start_slot = spec.compute_start_slot_at_epoch(epoch) + return _print_slot_range(spec, epoch_state, signed_blocks, start_slot, start_slot + spec.SLOTS_PER_EPOCH) - return ret + +def _print_block_tree(spec, root_state, signed_blocks): + start_slot = signed_blocks[0].message.slot + end_slot = signed_blocks[len(signed_blocks) - 1].message.slot + 1 + return _print_slot_range(spec, root_state, signed_blocks, start_slot, end_slot) -def _generate_blocks(spec, genesis_state, sm_links, rnd: random.Random, debug, block_parents) -> []: +def _generate_sm_link_tree(spec, genesis_state, sm_links, rnd: random.Random, debug) -> ([], BranchTip): """ Generates a sequence of blocks satisfying a tree of supermajority links specified in the sm_links list, i.e. a sequence of blocks with attestations required to create given supermajority links. @@ -441,7 +441,7 @@ def _generate_blocks(spec, genesis_state, sm_links, rnd: random.Random, debug, b # Abort the test if any sm_links constraint appears to be unreachable # because the justification of the source checkpoint hasn't been realized on chain yet if l.target == current_epoch and new_branch_tip.beacon_state.current_justified_checkpoint.epoch < l.source: - return [] + return [], new_branch_tip branch_tips[l] = new_branch_tip @@ -490,24 +490,28 @@ def _generate_blocks(spec, genesis_state, sm_links, rnd: random.Random, debug, b elif (sm_link.source != new_branch_tip.eventually_justified_checkpoint.epoch): # Abort the test as the justification of the source checkpoint can't be realized on chain # because of the lack of the block space - return [] + return [], new_branch_tip # If the fork won't be advanced in the future epochs # ensure 1) all yet not included attestations are included on chain by advancing it to epoch N+1 # 2) justification is realized by advancing it to epoch N+2 is_fork_advanced_in_future = any((l for l in sm_links if l.source == sm_link.target)) if sm_link.target == current_epoch and not is_fork_advanced_in_future: - # Advance to epoch N+1 - advanced_signed_blocks, advanced_branch_tip = _advance_branch_to_next_epoch(spec, new_branch_tip, - enable_attesting=False) - new_signed_blocks = new_signed_blocks + advanced_signed_blocks - - # Find a slot for which a proposer is among participants - state = advanced_branch_tip.beacon_state.copy() + advanced_branch_tip = new_branch_tip + + # Advance to N+1 if state.current_justified_checkpoint.epoch < eventually_justified_checkpoint.epoch + current_justified_epoch = new_branch_tip.beacon_state.current_justified_checkpoint.epoch + eventually_justified_epoch = new_branch_tip.eventually_justified_checkpoint.epoch + if current_justified_epoch < eventually_justified_epoch: + advanced_signed_blocks, advanced_branch_tip = _advance_branch_to_next_epoch(spec, new_branch_tip, + enable_attesting=False) + new_signed_blocks = new_signed_blocks + advanced_signed_blocks + + # Build a block in the next epoch to justify the target on chain + state = advanced_branch_tip.beacon_state while (spec.get_beacon_proposer_index(state) not in advanced_branch_tip.participants): next_slot(spec, state) - # Build a block in epoch > N+1 tip_block, _, _ = _produce_block(spec, state, []) new_signed_blocks.append(tip_block) @@ -554,20 +558,20 @@ def _generate_blocks(spec, genesis_state, sm_links, rnd: random.Random, debug, b branch_tips[sm_link] = new_branch_tip signed_blocks = signed_blocks + new_signed_blocks - # Process block tree model - block_tree = [] - highest_target_tip = branch_tips[highest_target_sm_link] + # Sort blocks by a slot + return sorted(signed_blocks, key=lambda b: b.message.slot), branch_tips[highest_target_sm_link] + + +def _generate_block_tree(spec, anchor_tip: BranchTip, rnd: random.Random, debug, block_parents) -> []: + signed_blocks = [] - attestations = highest_target_tip.attestations.copy() - post_states = [highest_target_tip.beacon_state.copy()] - current_slot = highest_target_tip.beacon_state.slot + attestations = anchor_tip.attestations.copy() + post_states = [anchor_tip.beacon_state.copy()] + current_slot = anchor_tip.beacon_state.slot block_index = 1 block_tree_tips = set([0]) while block_index < len(block_parents): - # Next slot - current_slot += 1 - # Propose a block if slot shouldn't be empty if rnd.randint(1, 100) > EMPTY_SLOTS_RATE: # Advance parent state to the current slot @@ -581,7 +585,7 @@ def _generate_blocks(spec, genesis_state, sm_links, rnd: random.Random, debug, b # Produce block proposer = spec.get_beacon_proposer_index(parent_state) signed_block, post_state, attestations = _produce_block(spec, parent_state, attestations) - block_tree.append(signed_block) + signed_blocks.append(signed_block) post_states.append(post_state) # Update tips @@ -609,16 +613,16 @@ def split_list(lst, n): # Prepend attestations to the list attestations = list(attestations_in_slot) + attestations - signed_blocks = signed_blocks + block_tree + # Next slot + current_slot += 1 if debug: print('\nblock_tree:') - print('blocks: ', _print_block_tree(spec, post_states[0], block_tree)) + print('blocks: ', _print_block_tree(spec, post_states[0], signed_blocks)) print(' ', 'state.current_justified_checkpoint:', '(epoch=' + str(post_states[len(post_states) - 1].current_justified_checkpoint.epoch) + ', root=' + str(post_states[len(post_states) - 1].current_justified_checkpoint.root)[:6] + ')') - # Sort blocks by a slot return sorted(signed_blocks, key=lambda b: b.message.slot) @@ -634,10 +638,8 @@ def _print_head(spec, store): @with_altair_and_later @spec_state_test -def test_sm_links_tree_model(spec, state, debug=False, seed=4, sm_links=None): +def test_sm_links_tree_model(spec, state, debug=False, seed=1, sm_links=None): block_parents = [0, 0, 0, 2, 2, 1, 1, 6, 6, 5, 5, 4, 7, 4, 3, 3] - sm_links = [(2, 3), (3, 4), (3, 6), (4, 7)] - debug = True # This test is mainly used for the test generation purposes # Thus seed and sm_links are provided by the generator @@ -652,20 +654,29 @@ def test_sm_links_tree_model(spec, state, debug=False, seed=4, sm_links=None): # Find a reachable solution trying with different seeds if needed # sm_links constraints may not have a solution beacause of the randomization affecting validator partitions signed_blocks = [] + highest_tip = BranchTip(state, [], [], state.current_justified_checkpoint) while True: if debug: print('\nseed:', seed) print('\nsm_links:', sm_links) rnd = random.Random(seed) - signed_blocks = _generate_blocks(spec, state, sm_links, rnd, debug, block_parents) + signed_blocks, highest_tip = _generate_sm_link_tree(spec, state, sm_links, rnd, debug) if len(signed_blocks) > 0: break - else: - new_seed = rnd.randint(1, 10000) - print('\nUnsatisfiable constraints: sm_links: ' + str(sm_links) + ', seed=' + str( - seed) + ', will retry with seed=' + str(new_seed)) - seed = new_seed + + new_seed = rnd.randint(1, 10000) + print('\nUnsatisfiable constraints: sm_links: ' + str(sm_links) + ', seed=' + str( + seed) + ', will retry with seed=' + str(new_seed)) + seed = new_seed + + # Block tree model + if block_parents is not None: + block_tree = _generate_block_tree(spec, highest_tip, rnd, debug, block_parents) + # Merge block_tree and sm_link_tree blocks + block_tree_root_slot = block_tree[0].message.slot + signed_blocks = [sb for sb in signed_blocks if sb.message.slot < block_tree_root_slot] + signed_blocks = signed_blocks + block_tree # Yield run parameters yield 'seed', 'meta', seed @@ -688,6 +699,6 @@ def test_sm_links_tree_model(spec, state, debug=False, seed=4, sm_links=None): assert store.blocks[block_root] == block if debug: - print('head: ' + _print_head(spec, store)) + print(' head: ' + _print_head(spec, store)) yield 'steps', test_steps From c46b9a33b0cfac78869e91104d6aedbfd243207d Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 14 Mar 2024 17:49:21 +0600 Subject: [PATCH 017/111] Add block cover instantiator --- .../fork_choice/test_sm_links_tree_model.py | 262 ++++++++++++++++-- .../filter_block_tree_generator.py | 146 ++++++++++ .../model/minizinc/Block_cover3.mzn | 65 +++++ 3 files changed, 447 insertions(+), 26 deletions(-) create mode 100644 tests/generators/fork_choice_generated/filter_block_tree_generator.py create mode 100644 tests/generators/fork_choice_generated/model/minizinc/Block_cover3.mzn diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py index 347a31ecda..61d0a792d8 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py @@ -63,6 +63,12 @@ def __init__(self, beacon_state, attestations, participants, eventually_justifie self.participants = participants self.eventually_justified_checkpoint = eventually_justified_checkpoint + def copy(self): + return BranchTip(self.beacon_state.copy(), + self.attestations.copy(), + self.participants.copy(), + self.eventually_justified_checkpoint) + def _justifying_participation_rate(rnd: random.Random): """ @@ -180,6 +186,10 @@ def _compute_validator_partitions(spec, branch_tips, current_links, current_epoc return participants +def _get_eligible_attestations(spec, state, attestations) -> []: + return [a for a in attestations if state.slot <= a.data.slot + spec.SLOTS_PER_EPOCH] + + def _produce_block(spec, state, attestations): """ Produces a block including as many attestations as it is possible. @@ -187,7 +197,7 @@ def _produce_block(spec, state, attestations): """ # Filter out too old attestastions (TODO relax condition for Deneb) - eligible_attestations = [a for a in attestations if state.slot <= a.data.slot + spec.SLOTS_PER_EPOCH] + eligible_attestations = _get_eligible_attestations(spec, state, attestations) # Prepare attestations attestation_in_block = eligible_attestations[:spec.MAX_ATTESTATIONS] @@ -208,7 +218,7 @@ def _produce_block(spec, state, attestations): return signed_block, post_state, not_included_attestations -def _attest_to_slot(spec, state, slot_to_attest, participants_filter=None): +def _attest_to_slot(spec, state, slot_to_attest, participants_filter=None) -> []: """ Creates attestation is a slot respecting participating validators. :return: produced attestations @@ -371,6 +381,33 @@ def _print_block_tree(spec, root_state, signed_blocks): return _print_slot_range(spec, root_state, signed_blocks, start_slot, end_slot) +def _advance_state_to_anchor_epoch(spec, state, anchor_epoch, debug) -> ([], BranchTip): + signed_blocks = [] + + genesis_tip = BranchTip(state.copy(), [], [*range(0, len(state.validators))], + state.current_justified_checkpoint) + + # Advance the state to the anchor_epoch + anchor_tip = genesis_tip + for epoch in range(spec.GENESIS_EPOCH, anchor_epoch + 1): + pre_state = anchor_tip.beacon_state + new_signed_blocks, anchor_tip = _advance_branch_to_next_epoch(spec, anchor_tip) + signed_blocks = signed_blocks + new_signed_blocks + if debug: + post_state = anchor_tip.beacon_state + print('\nepoch', str(epoch) + ':') + print('branch(*, *):', _print_epoch(spec, pre_state, new_signed_blocks)) + print(' ', len(anchor_tip.participants), 'participants:', anchor_tip.participants) + print(' ', 'state.current_justified_checkpoint:', + '(epoch=' + str(post_state.current_justified_checkpoint.epoch) + + ', root=' + str(post_state.current_justified_checkpoint.root)[:6] + ')') + print(' ', 'eventually_justified_checkpoint:', + '(epoch=' + str(anchor_tip.eventually_justified_checkpoint.epoch) + + ', root=' + str(anchor_tip.eventually_justified_checkpoint.root)[:6] + ')') + + return signed_blocks, anchor_tip + + def _generate_sm_link_tree(spec, genesis_state, sm_links, rnd: random.Random, debug) -> ([], BranchTip): """ Generates a sequence of blocks satisfying a tree of supermajority links specified in the sm_links list, @@ -396,32 +433,10 @@ def _generate_sm_link_tree(spec, genesis_state, sm_links, rnd: random.Random, de # because the protocol can't justify in epoch GENESIS_EPOCH + 1 assert len([sm_link for sm_link in sm_links if sm_link.source == spec.GENESIS_EPOCH + 1]) == 0 - state = genesis_state.copy() - signed_blocks = [] - - # Find anchor epoch and check it + # Find anchor epoch anchor_epoch = min(sm_links, key=lambda l: l.source).source - genesis_tip = BranchTip(state.copy(), [], [*range(0, len(state.validators))], - state.current_justified_checkpoint) - - # Advance the state to the anchor_epoch - anchor_tip = genesis_tip - for epoch in range(spec.GENESIS_EPOCH, anchor_epoch + 1): - pre_state = anchor_tip.beacon_state - new_signed_blocks, anchor_tip = _advance_branch_to_next_epoch(spec, anchor_tip) - signed_blocks = signed_blocks + new_signed_blocks - if debug: - post_state = anchor_tip.beacon_state - print('\nepoch', str(epoch) + ':') - print('branch(*, *):', _print_epoch(spec, pre_state, new_signed_blocks)) - print(' ', len(anchor_tip.participants), 'participants:', anchor_tip.participants) - print(' ', 'state.current_justified_checkpoint:', - '(epoch=' + str(post_state.current_justified_checkpoint.epoch) + - ', root=' + str(post_state.current_justified_checkpoint.root)[:6] + ')') - print(' ', 'eventually_justified_checkpoint:', - '(epoch=' + str(anchor_tip.eventually_justified_checkpoint.epoch) + - ', root=' + str(anchor_tip.eventually_justified_checkpoint.root)[:6] + ')') + signed_blocks, anchor_tip = _advance_state_to_anchor_epoch(spec, genesis_state, anchor_epoch, debug) # branch_tips hold the most recent state, validator partition and not included attestations for every fork # Initialize branch tips with the anchor tip @@ -702,3 +717,198 @@ def test_sm_links_tree_model(spec, state, debug=False, seed=1, sm_links=None): print(' head: ' + _print_head(spec, store)) yield 'steps', test_steps + + +def _generate_filter_block_tree(spec, genesis_state, block_epochs, ancestors, previous_justifications, + current_justifications, rnd: random.Random, debug) -> ([], []): + anchor_epoch = block_epochs[0] + + signed_blocks, anchor_tip = _advance_state_to_anchor_epoch(spec, genesis_state, anchor_epoch, debug) + + block_tips = [None for _ in range(0, len(block_epochs))] + # Initialize with the anchor block + block_tips[0] = anchor_tip + + JUSTIFYING_SLOT = 2 * spec.SLOTS_PER_EPOCH // 3 + 1 + + for epoch in range(anchor_epoch + 1, max(block_epochs) + 1): + current_blocks = [i for i, e in enumerate(block_epochs) if e == epoch] + if len(current_blocks) == 0: + continue + + # There should be enough slots to propose all blocks + assert (spec.SLOTS_PER_EPOCH - JUSTIFYING_SLOT) >= len( + current_blocks), "Unsatisfiable constraints: not enough slots to propose all block: " + str(current_blocks) + + # Case 2. Blocks are from disjoint subtrees -- not supported yet + assert len( + set([a for i, a in enumerate(ancestors) if i in current_blocks])) == 1, 'Disjoint trees are not supported' + + # Case 1. Blocks have common ancestor + a = ancestors[current_blocks[0]] + ancestor_tip = block_tips[a].copy() + + state = ancestor_tip.beacon_state + attestations = ancestor_tip.attestations + threshold_slot = spec.compute_start_slot_at_epoch(epoch) + JUSTIFYING_SLOT + + # Build the chain up to but excluding a block that will justify current checkpoint + while (state.slot < threshold_slot): + # Do not include attestations into blocks + if (state.slot < spec.compute_start_slot_at_epoch(epoch)): + new_block, state, _ = _produce_block(spec, state, []) + signed_blocks.append(new_block) + else: + new_block, state, attestations = _produce_block(spec, state, attestations) + signed_blocks.append(new_block) + + # Attest + curr_slot_attestations = _attest_to_slot(spec, state, state.slot) + attestations = curr_slot_attestations + attestations + + # Next slot + next_slot(spec, state) + + common_state = state + + # Assumption: one block is enough to satisfy previous_justifications[b] and current_justifications[b], + # i.e. block capacity is enough to accommodate attestations to justify previus and current epoch checkpoints + # if that needed. Considering that most of attestations were already included into the common chain prefix, + # we assume it is possible + empty_slot_count = spec.SLOTS_PER_EPOCH - JUSTIFYING_SLOT - len(current_blocks) + block_distribution = current_blocks.copy() + [-1 for _ in range(0, empty_slot_count)] + + # Randomly distribute blocks across slots + rnd.shuffle(block_distribution) + + for index, block in enumerate(block_distribution): + slot = threshold_slot + index + state = common_state.copy() + + # Advance state to the slot + if state.slot < slot: + transition_to(spec, state, slot) + + # Propose a block if slot isn't empty + block_attestations = [] + if block > -1: + previous_epoch_attestations = [a for a in attestations if + epoch == spec.compute_epoch_at_slot(a.data.slot) + 1] + current_epoch_attestations = [a for a in attestations if epoch == spec.compute_epoch_at_slot(a.data.slot)] + if (previous_justifications[block]): + block_attestations = block_attestations + previous_epoch_attestations + if (current_justifications[block]): + block_attestations = block_attestations + current_epoch_attestations + + # Propose block + new_block, state, _ = _produce_block(spec, state, block_attestations) + signed_blocks.append(new_block) + + # Attest + # TODO pick a random tip to make attestation with if the slot is empty + curr_slot_attestations = _attest_to_slot(spec, state, state.slot) + attestations = curr_slot_attestations + attestations + + # Next slot + next_slot(spec, state) + + if block > -1: + not_included_attestations = [a for a in attestations if a not in block_attestations] + + check_up_state = state.copy() + spec.process_justification_and_finalization(check_up_state) + + if current_justifications[block]: + assert check_up_state.current_justified_checkpoint.epoch == epoch, 'Unexpected current_jusitified_checkpoint.epoch: ' + str( + check_up_state.current_justified_checkpoint.epoch) + ' != ' + str(epoch) + elif previous_justifications[block]: + assert check_up_state.current_justified_checkpoint.epoch + 1 == epoch, 'Unexpected current_jusitified_checkpoint.epoch: ' + str( + check_up_state.current_justified_checkpoint.epoch) + ' != ' + str(epoch - 1) + + block_tips[block] = BranchTip(state, not_included_attestations, [*range(0, len(state.validators))], + check_up_state.current_justified_checkpoint) + + return signed_blocks, block_tips + + +@with_altair_and_later +@spec_state_test +def test_filter_block_tree_model(spec, state, model_params=None, debug=False, seed=1): + if model_params is None: + return + + if debug: + print('\nseed:', seed) + print('\nmodel_params:', str(model_params)) + + block_epochs = model_params['block_epochs'] + ancestors = model_params['ancestors'] + previous_justifications = model_params['previous_justifications'] + current_justifications = model_params['current_justifications'] + current_epoch = model_params['current_epoch'] + + store_justified_epoch = model_params['store_justified_epoch'] + target_block = model_params['target_block'] + + anchor_epoch = block_epochs[0] + + # Ensure that epoch(block) = epoch(ancenstor) + 1 + for b in range(1, len(block_epochs)): + assert block_epochs[b] == block_epochs[ancestors[b]] + 1, 'epoch(' + str(b) + ') != epoch(' + str( + ancestors[b]) + ')' + + # Ensure that a descendant doesn't attempt to justify the previous epoch checkpoint + # if it has already been justified by its ancestor + for b in range(1, len(block_epochs)): + if previous_justifications[b]: + a = ancestors[b] + assert not current_justifications[a], str(b) + ' attempts to justify already justified epoch' + + rnd = random.Random(seed) + signed_blocks, post_block_tips = _generate_filter_block_tree(spec, + state, + block_epochs, + ancestors, + previous_justifications, + current_justifications, + rnd, + debug) + + # Yield run parameters + yield 'seed', 'meta', seed + yield 'model_params', 'meta', model_params + + test_steps = [] + # Store initialization + store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) + yield 'anchor_state', state + yield 'anchor_block', anchor_block + current_time = state.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time + on_tick_and_append_step(spec, store, current_time, test_steps) + assert store.time == current_time + + # Apply generated blocks + for signed_block in signed_blocks: + block = signed_block.message + yield from tick_and_add_block(spec, store, signed_block, test_steps) + block_root = block.hash_tree_root() + assert store.blocks[block_root] == block + + # Advance the store to the current epoch + current_epoch_time = state.genesis_time + spec.compute_start_slot_at_epoch( + current_epoch) * spec.config.SECONDS_PER_SLOT + if store.time < current_epoch_time: + on_tick_and_append_step(spec, store, current_epoch_time, test_steps) + + # Ensure the time is set correct + assert store.time == current_epoch_time + # Ensure the store.justified_checkpoint.epoch is as expected + assert store.justified_checkpoint.epoch == store_justified_epoch + # Ensure the target block is in filtered_blocks + filtered_block_roots = list(spec.get_filtered_block_tree(store).keys()) + target_block_root = spec.hash_tree_root(post_block_tips[target_block].beacon_state.latest_block_header) + assert target_block_root in filtered_block_roots + + test_steps.append({'property_checks': {'filtered_block_roots': [str(r) for r in filtered_block_roots]}}) + + yield 'steps', test_steps diff --git a/tests/generators/fork_choice_generated/filter_block_tree_generator.py b/tests/generators/fork_choice_generated/filter_block_tree_generator.py new file mode 100644 index 0000000000..4b680d00b8 --- /dev/null +++ b/tests/generators/fork_choice_generated/filter_block_tree_generator.py @@ -0,0 +1,146 @@ +from eth2spec.test.helpers.constants import ALTAIR +from eth2spec.gen_helpers.gen_base import gen_runner +from eth2spec.test.helpers.constants import MINIMAL +from eth2spec.gen_helpers.gen_base.gen_typing import TestCase, TestProvider +from typing import Iterable +from importlib import import_module +from eth2spec.utils import bls +from eth2spec.test.helpers.typing import SpecForkName, PresetBaseName +from minizinc import Instance, Model, Solver +import random + +BLS_ACTIVE = False +GENERATOR_NAME = 'filter_block_tree' + + +def _import_test_fn(): + src = import_module('eth2spec.test.phase0.fork_choice.test_sm_links_tree_model') + print("generating test vectors from tests source: %s" % src.__name__) + return getattr(src, 'test_filter_block_tree_model') + + +def _find_model_solutions(anchor_epoch: int, + store_justified_epoch_equal_zero: bool, + block_voting_source_epoch_equal_store_justified_epoch: bool, + block_voting_source_epoch_plus_two_greater_or_equal_current_epoch: bool, + block_is_leaf: bool, + block_is_justified_root_descendant: bool) -> Iterable[Iterable[tuple]]: + block_cover3 = Model('./model/minizinc/Block_cover3.mzn') + solver = Solver.lookup("gecode") + instance = Instance(solver, block_cover3) + instance['AE'] = anchor_epoch + instance['store_je_eq_zero'] = store_justified_epoch_equal_zero + instance['block_vse_eq_store_je'] = block_voting_source_epoch_equal_store_justified_epoch + instance['block_vse_plus_two_ge_curr_e'] = block_voting_source_epoch_plus_two_greater_or_equal_current_epoch + instance['block_is_leaf'] = block_is_leaf + instance['block_is_justified_descendant'] = block_is_justified_root_descendant + + result = instance.solve(nr_solutions=5) + + for s in result.solution: + max_block = s.max_block + yield {'block_epochs': s.es[:max_block + 1], + 'ancestors': s.ancestors[:max_block + 1], + 'previous_justifications': s.prevs[:max_block + 1], + 'current_justifications': s.currs[:max_block + 1], + 'current_epoch': s.curr_e, + 'store_justified_epoch': s.store_je, + 'target_block': s.target_block} + + +def _create_providers(forks: Iterable[SpecForkName], + presets: Iterable[PresetBaseName], + debug: bool, + initial_seed: int, + anchor_epoch: int, + number_of_variations: int) -> Iterable[TestProvider]: + def prepare_fn() -> None: + bls.use_milagro() + return + + def make_cases_fn() -> Iterable[TestCase]: + test_fn = _import_test_fn() + + seeds = [initial_seed] + if number_of_variations > 1: + rnd = random.Random(initial_seed) + seeds = [rnd.randint(1, 10000) for _ in range(number_of_variations)] + seeds[0] = initial_seed + + solutions = _find_model_solutions(anchor_epoch=anchor_epoch, + store_justified_epoch_equal_zero=False, + block_voting_source_epoch_equal_store_justified_epoch=False, + block_voting_source_epoch_plus_two_greater_or_equal_current_epoch=True, + block_is_leaf=True, + block_is_justified_root_descendant=True) + + for i, solution in enumerate(solutions): + for seed in seeds: + for fork_name in forks: + for preset_name in presets: + yield TestCase(fork_name=fork_name, + preset_name=preset_name, + runner_name=GENERATOR_NAME, + handler_name='filter_block_tree_model', + suite_name='fork_choice', + case_name='filter_block_tree_model_' + str(i) + '_' + str(seed), + case_fn=lambda: test_fn(generator_mode=True, + phase=fork_name, + preset=preset_name, + bls_active=BLS_ACTIVE, + debug=debug, + seed=seed, + model_params=solution)) + + yield TestProvider(prepare=prepare_fn, make_cases=make_cases_fn) + + +if __name__ == "__main__": + forks = [ALTAIR] + presets = [MINIMAL] + + arg_parser = gen_runner.create_arg_parser(GENERATOR_NAME) + + arg_parser.add_argument( + '--fc-gen-debug', + dest='fc_gen_debug', + action='store_true', + default=False, + required=False, + help='If set provides debug output and enable additional checks for generated chains', + ) + arg_parser.add_argument( + '--fc-gen-seed', + dest='fc_gen_seed', + default=1, + type=int, + required=False, + help='Provides randomizer with initial seed' + ) + arg_parser.add_argument( + '--fc-gen-variations', + dest='fc_gen_variations', + default=1, + type=int, + required=False, + help='Number of random variations per each solution' + ) + arg_parser.add_argument( + '--fc-gen-anchor-epoch', + dest='fc_gen_anchor_epoch', + default=0, + type=int, + required=False, + help='Anchor epoch' + ) + + args = arg_parser.parse_args() + + gen_runner.run_generator(GENERATOR_NAME, + _create_providers(forks=forks, + presets=presets, + debug=args.fc_gen_debug, + initial_seed=args.fc_gen_seed, + anchor_epoch=args.fc_gen_anchor_epoch, + number_of_variations=args.fc_gen_variations), + arg_parser) diff --git a/tests/generators/fork_choice_generated/model/minizinc/Block_cover3.mzn b/tests/generators/fork_choice_generated/model/minizinc/Block_cover3.mzn new file mode 100644 index 0000000000..304490db53 --- /dev/null +++ b/tests/generators/fork_choice_generated/model/minizinc/Block_cover3.mzn @@ -0,0 +1,65 @@ +include "globals.mzn"; + +int: AE; +int: MB = 4; + +set of int: EPOCH = AE..(AE+MB-1); +set of int: BLOCK = 0..MB; + +type BS = record(EPOCH: e, EPOCH: je, EPOCH: uje); + + +array[BLOCK] of var EPOCH: es; +array[BLOCK] of var EPOCH: pjes; +array[BLOCK] of var EPOCH: cjes; +array[BLOCK] of var EPOCH: ujes; +array[BLOCK] of var bool: prevs; +array[BLOCK] of var bool: currs; +array[BLOCK] of var BLOCK: ancestors; + +function var EPOCH: n_e(var BLOCK: b) = es[b] + 1; +function var EPOCH: n_pje(var BLOCK: b) = cjes[b]; +function var EPOCH: n_cje(var BLOCK: b) = + if currs[b] then + es[b] + elseif prevs[b] then + es[b] - 1 + else + cjes[b] + endif; + +constraint es[0] == AE; +constraint pjes[0] == AE /\ cjes[0] == AE; +constraint forall(b in BLOCK)(if b > 0 then ancestors[b] < b else ancestors[b] == b endif); + +var BLOCK: max_block; + +constraint forall(b in BLOCK where b <= max_block /\ b > 0)(n_e(ancestors[b]) == es[b] /\ n_pje(ancestors[b]) == pjes[b] /\ n_cje(ancestors[b]) == cjes[b]); +constraint forall(b in BLOCK where b <= max_block)(ujes[b] == n_cje(b)); + +function var EPOCH: get_vse(var BLOCK: b) = + if es[b] < curr_e then n_cje(b) else cjes[b] endif; + +var EPOCH: curr_e; +var EPOCH: store_je; + +constraint forall(b in BLOCK where b <= max_block)(es[b] <= curr_e); +constraint forall(b in BLOCK where b <= max_block)(get_vse(b) <= store_je); +constraint exists(b in BLOCK where b <= max_block)(get_vse(b) == store_je); + +predicate is_leaf(var BLOCK: b) = not exists(c in BLOCK where c <= max_block)(ancestors[c] == b); + +var BLOCK: target_block; +constraint target_block <= max_block; + +bool: store_je_eq_zero; +bool: block_vse_eq_store_je; +bool: block_vse_plus_two_ge_curr_e; +bool: block_is_leaf; +bool: block_is_justified_descendant; + +constraint block_vse_eq_store_je <-> get_vse(target_block) == store_je; +constraint block_vse_plus_two_ge_curr_e <-> get_vse(target_block) + 2 >= curr_e; +constraint block_is_leaf <-> is_leaf(target_block); +constraint block_is_justified_descendant <-> es[target_block] >= store_je; +constraint store_je_eq_zero <-> store_je == 0; From 84c658bfdc4a2d20f3586d85cd42cec4d978598e Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Sat, 30 Mar 2024 01:36:36 +0600 Subject: [PATCH 018/111] Update block_cover3 model and fix instantiator --- .../fork_choice/test_sm_links_tree_model.py | 85 +++++++++++++------ .../filter_block_tree_generator.py | 47 +++++++--- .../model/minizinc/Block_cover3.mzn | 30 ++++--- 3 files changed, 111 insertions(+), 51 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py index 61d0a792d8..d36a3bbdcf 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py @@ -46,6 +46,7 @@ EMPTY_SLOTS_RATE = 3 MAX_TIPS_TO_ATTEST = 2 + class SmLink(tuple): @property def source(self): @@ -719,7 +720,7 @@ def test_sm_links_tree_model(spec, state, debug=False, seed=1, sm_links=None): yield 'steps', test_steps -def _generate_filter_block_tree(spec, genesis_state, block_epochs, ancestors, previous_justifications, +def _generate_filter_block_tree(spec, genesis_state, block_epochs, parents, previous_justifications, current_justifications, rnd: random.Random, debug) -> ([], []): anchor_epoch = block_epochs[0] @@ -738,14 +739,14 @@ def _generate_filter_block_tree(spec, genesis_state, block_epochs, ancestors, pr # There should be enough slots to propose all blocks assert (spec.SLOTS_PER_EPOCH - JUSTIFYING_SLOT) >= len( - current_blocks), "Unsatisfiable constraints: not enough slots to propose all block: " + str(current_blocks) + current_blocks), "Unsatisfiable constraints: not enough slots to propose all blocks: " + str(current_blocks) # Case 2. Blocks are from disjoint subtrees -- not supported yet assert len( - set([a for i, a in enumerate(ancestors) if i in current_blocks])) == 1, 'Disjoint trees are not supported' + set([a for i, a in enumerate(parents) if i in current_blocks])) == 1, 'Disjoint trees are not supported' # Case 1. Blocks have common ancestor - a = ancestors[current_blocks[0]] + a = parents[current_blocks[0]] ancestor_tip = block_tips[a].copy() state = ancestor_tip.beacon_state @@ -794,7 +795,8 @@ def _generate_filter_block_tree(spec, genesis_state, block_epochs, ancestors, pr if block > -1: previous_epoch_attestations = [a for a in attestations if epoch == spec.compute_epoch_at_slot(a.data.slot) + 1] - current_epoch_attestations = [a for a in attestations if epoch == spec.compute_epoch_at_slot(a.data.slot)] + current_epoch_attestations = [a for a in attestations if + epoch == spec.compute_epoch_at_slot(a.data.slot)] if (previous_justifications[block]): block_attestations = block_attestations + previous_epoch_attestations if (current_justifications[block]): @@ -837,42 +839,41 @@ def test_filter_block_tree_model(spec, state, model_params=None, debug=False, se if model_params is None: return - if debug: - print('\nseed:', seed) - print('\nmodel_params:', str(model_params)) + print('\nseed:', seed) + print('predicates:', str(model_params['predicates'])) + print('model_params:', str(model_params)) block_epochs = model_params['block_epochs'] - ancestors = model_params['ancestors'] + parents = model_params['parents'] previous_justifications = model_params['previous_justifications'] current_justifications = model_params['current_justifications'] - current_epoch = model_params['current_epoch'] store_justified_epoch = model_params['store_justified_epoch'] target_block = model_params['target_block'] anchor_epoch = block_epochs[0] - # Ensure that epoch(block) = epoch(ancenstor) + 1 + # Ensure that epoch(block) == epoch(parent) + 1 for b in range(1, len(block_epochs)): - assert block_epochs[b] == block_epochs[ancestors[b]] + 1, 'epoch(' + str(b) + ') != epoch(' + str( - ancestors[b]) + ')' + assert block_epochs[b] == block_epochs[parents[b]] + 1, 'epoch(' + str(b) + ') != epoch(' + str( + parents[b]) + ') + 1, block_epochs=' + str(block_epochs) + ', parents=' + str(parents) # Ensure that a descendant doesn't attempt to justify the previous epoch checkpoint # if it has already been justified by its ancestor for b in range(1, len(block_epochs)): if previous_justifications[b]: - a = ancestors[b] + a = parents[b] assert not current_justifications[a], str(b) + ' attempts to justify already justified epoch' rnd = random.Random(seed) signed_blocks, post_block_tips = _generate_filter_block_tree(spec, - state, - block_epochs, - ancestors, - previous_justifications, - current_justifications, - rnd, - debug) + state, + block_epochs, + parents, + previous_justifications, + current_justifications, + rnd, + debug) # Yield run parameters yield 'seed', 'meta', seed @@ -894,20 +895,50 @@ def test_filter_block_tree_model(spec, state, model_params=None, debug=False, se block_root = block.hash_tree_root() assert store.blocks[block_root] == block - # Advance the store to the current epoch - current_epoch_time = state.genesis_time + spec.compute_start_slot_at_epoch( - current_epoch) * spec.config.SECONDS_PER_SLOT + # Advance the store to the current epoch as per model + current_epoch_slot = spec.compute_start_slot_at_epoch(model_params['current_epoch']) + current_epoch_time = state.genesis_time + current_epoch_slot * spec.config.SECONDS_PER_SLOT if store.time < current_epoch_time: on_tick_and_append_step(spec, store, current_epoch_time, test_steps) - # Ensure the time is set correct - assert store.time == current_epoch_time + current_epoch = spec.get_current_store_epoch(store) + # Ensure the epoch is correct + assert current_epoch == model_params['current_epoch'] # Ensure the store.justified_checkpoint.epoch is as expected assert store.justified_checkpoint.epoch == store_justified_epoch # Ensure the target block is in filtered_blocks filtered_block_roots = list(spec.get_filtered_block_tree(store).keys()) target_block_root = spec.hash_tree_root(post_block_tips[target_block].beacon_state.latest_block_header) - assert target_block_root in filtered_block_roots + + # Check predicates + predicates = model_params['predicates'] + if predicates['store_je_eq_zero']: + assert store.justified_checkpoint.epoch == spec.GENESIS_EPOCH, "store_je_eq_zero not satisfied" + + if predicates['block_is_leaf']: + assert not any( + b for b in store.blocks.values() if b.parent_root == target_block_root), "block_is_leaf not satisfied" + else: + assert any( + b for b in store.blocks.values() if b.parent_root == target_block_root), "block_is_leaf not satisfied" + + voting_source = spec.get_voting_source(store, target_block_root) + if predicates['block_vse_eq_store_je']: + assert voting_source.epoch == store.justified_checkpoint.epoch, "block_vse_eq_store_je not satisfied" + else: + assert voting_source.epoch != store.justified_checkpoint.epoch, "block_vse_eq_store_je not satisfied" + + if predicates['block_vse_plus_two_ge_curr_e']: + assert voting_source.epoch + 2 >= current_epoch, "block_vse_plus_two_ge_curr_e not satisfied" + else: + assert voting_source.epoch + 2 < current_epoch, "block_vse_plus_two_ge_curr_e not satisfied" + + # Ensure the target block is in filtered blocks if it is a leaf and eligible + if (predicates['block_is_leaf'] + and (predicates['store_je_eq_zero'] + or predicates['block_vse_eq_store_je'] + or predicates['block_vse_plus_two_ge_curr_e'])): + assert target_block_root in filtered_block_roots test_steps.append({'property_checks': {'filtered_block_roots': [str(r) for r in filtered_block_roots]}}) diff --git a/tests/generators/fork_choice_generated/filter_block_tree_generator.py b/tests/generators/fork_choice_generated/filter_block_tree_generator.py index 4b680d00b8..06e3145993 100644 --- a/tests/generators/fork_choice_generated/filter_block_tree_generator.py +++ b/tests/generators/fork_choice_generated/filter_block_tree_generator.py @@ -23,8 +23,7 @@ def _find_model_solutions(anchor_epoch: int, store_justified_epoch_equal_zero: bool, block_voting_source_epoch_equal_store_justified_epoch: bool, block_voting_source_epoch_plus_two_greater_or_equal_current_epoch: bool, - block_is_leaf: bool, - block_is_justified_root_descendant: bool) -> Iterable[Iterable[tuple]]: + block_is_leaf: bool) -> []: block_cover3 = Model('./model/minizinc/Block_cover3.mzn') solver = Solver.lookup("gecode") instance = Instance(solver, block_cover3) @@ -33,19 +32,27 @@ def _find_model_solutions(anchor_epoch: int, instance['block_vse_eq_store_je'] = block_voting_source_epoch_equal_store_justified_epoch instance['block_vse_plus_two_ge_curr_e'] = block_voting_source_epoch_plus_two_greater_or_equal_current_epoch instance['block_is_leaf'] = block_is_leaf - instance['block_is_justified_descendant'] = block_is_justified_root_descendant result = instance.solve(nr_solutions=5) + output = [] for s in result.solution: max_block = s.max_block - yield {'block_epochs': s.es[:max_block + 1], - 'ancestors': s.ancestors[:max_block + 1], + output.append({'block_epochs': s.es[:max_block + 1], + 'parents': s.parents[:max_block + 1], 'previous_justifications': s.prevs[:max_block + 1], 'current_justifications': s.currs[:max_block + 1], 'current_epoch': s.curr_e, 'store_justified_epoch': s.store_je, - 'target_block': s.target_block} + 'target_block': s.target_block, + 'predicates': { + 'store_je_eq_zero': store_justified_epoch_equal_zero, + 'block_vse_eq_store_je': block_voting_source_epoch_equal_store_justified_epoch, + 'block_vse_plus_two_ge_curr_e': block_voting_source_epoch_plus_two_greater_or_equal_current_epoch, + 'block_is_leaf': block_is_leaf + }}) + + return output def _create_providers(forks: Iterable[SpecForkName], @@ -67,12 +74,28 @@ def make_cases_fn() -> Iterable[TestCase]: seeds = [rnd.randint(1, 10000) for _ in range(number_of_variations)] seeds[0] = initial_seed - solutions = _find_model_solutions(anchor_epoch=anchor_epoch, - store_justified_epoch_equal_zero=False, - block_voting_source_epoch_equal_store_justified_epoch=False, - block_voting_source_epoch_plus_two_greater_or_equal_current_epoch=True, - block_is_leaf=True, - block_is_justified_root_descendant=True) + solutions = [] + + for store_je_eq_zero in [True, False]: + for block_vse_eq_store_je in [True, False]: + for block_vse_plus_two_ge_curr_e in [True, False]: + for block_is_leaf in [True, False]: + if store_je_eq_zero and not block_vse_eq_store_je: + continue + results = _find_model_solutions(anchor_epoch=0 if store_je_eq_zero else anchor_epoch, + store_justified_epoch_equal_zero=store_je_eq_zero, + block_voting_source_epoch_equal_store_justified_epoch=block_vse_eq_store_je, + block_voting_source_epoch_plus_two_greater_or_equal_current_epoch=block_vse_plus_two_ge_curr_e, + block_is_leaf=block_is_leaf) + print('\n\n') + print(['store_je_eq_zero=' + str(store_je_eq_zero), + 'block_vse_eq_store_je=' + str(block_vse_eq_store_je), + 'block_vse_plus_two_ge_curr_e=' + str(block_vse_plus_two_ge_curr_e), + 'block_is_leaf=' + str(block_is_leaf)]) + for r in results: + print(r) + + solutions = solutions + results for i, solution in enumerate(solutions): for seed in seeds: diff --git a/tests/generators/fork_choice_generated/model/minizinc/Block_cover3.mzn b/tests/generators/fork_choice_generated/model/minizinc/Block_cover3.mzn index 304490db53..24be71e814 100644 --- a/tests/generators/fork_choice_generated/model/minizinc/Block_cover3.mzn +++ b/tests/generators/fork_choice_generated/model/minizinc/Block_cover3.mzn @@ -15,7 +15,7 @@ array[BLOCK] of var EPOCH: cjes; array[BLOCK] of var EPOCH: ujes; array[BLOCK] of var bool: prevs; array[BLOCK] of var bool: currs; -array[BLOCK] of var BLOCK: ancestors; +array[BLOCK] of var BLOCK: parents; function var EPOCH: n_e(var BLOCK: b) = es[b] + 1; function var EPOCH: n_pje(var BLOCK: b) = cjes[b]; @@ -30,12 +30,10 @@ function var EPOCH: n_cje(var BLOCK: b) = constraint es[0] == AE; constraint pjes[0] == AE /\ cjes[0] == AE; -constraint forall(b in BLOCK)(if b > 0 then ancestors[b] < b else ancestors[b] == b endif); +constraint forall(b in BLOCK)(if b > 0 then parents[b] < b else parents[b] == b endif); -var BLOCK: max_block; - -constraint forall(b in BLOCK where b <= max_block /\ b > 0)(n_e(ancestors[b]) == es[b] /\ n_pje(ancestors[b]) == pjes[b] /\ n_cje(ancestors[b]) == cjes[b]); -constraint forall(b in BLOCK where b <= max_block)(ujes[b] == n_cje(b)); +constraint forall(b in BLOCK where b > 0)(n_e(parents[b]) == es[b] /\ n_pje(parents[b]) == pjes[b] /\ n_cje(parents[b]) == cjes[b]); +constraint forall(b in BLOCK)(ujes[b] == n_cje(b)); function var EPOCH: get_vse(var BLOCK: b) = if es[b] < curr_e then n_cje(b) else cjes[b] endif; @@ -43,23 +41,31 @@ function var EPOCH: get_vse(var BLOCK: b) = var EPOCH: curr_e; var EPOCH: store_je; -constraint forall(b in BLOCK where b <= max_block)(es[b] <= curr_e); -constraint forall(b in BLOCK where b <= max_block)(get_vse(b) <= store_je); -constraint exists(b in BLOCK where b <= max_block)(get_vse(b) == store_je); +constraint forall(b in BLOCK)(es[b] <= curr_e); +constraint forall(b in BLOCK)(get_vse(b) <= store_je); +constraint exists(b in BLOCK)(get_vse(b) == store_je); -predicate is_leaf(var BLOCK: b) = not exists(c in BLOCK where c <= max_block)(ancestors[c] == b); +predicate is_leaf(var BLOCK: b) = not exists(child in BLOCK where child < max_block)(parents[child] == b); var BLOCK: target_block; +var BLOCK: max_block; + +constraint get_vse(max_block) == store_je; constraint target_block <= max_block; bool: store_je_eq_zero; +bool: store_fe_eq_zero = true; bool: block_vse_eq_store_je; bool: block_vse_plus_two_ge_curr_e; bool: block_is_leaf; -bool: block_is_justified_descendant; +bool: block_is_store_jb_descendant = true; +bool: block_is_store_fb_descendant = true; constraint block_vse_eq_store_je <-> get_vse(target_block) == store_je; constraint block_vse_plus_two_ge_curr_e <-> get_vse(target_block) + 2 >= curr_e; constraint block_is_leaf <-> is_leaf(target_block); -constraint block_is_justified_descendant <-> es[target_block] >= store_je; constraint store_je_eq_zero <-> store_je == 0; +% constraint block_is_justified_descendant <-> common_ancestor(target_block, store_jb) == store_jb; + +% constraint store_fe_eq_zero <-> store_fe == 0; +% constraint block_is_finalized_descendant <-> common_ancestor(target_block, store_fb) == store_fb; From e4b4556732ca4718f8365ac901d29c6284d7976c Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 2 Apr 2024 01:22:51 +0400 Subject: [PATCH 019/111] Sort tips before sampling --- .../test/phase0/fork_choice/test_sm_links_tree_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py index d36a3bbdcf..fff3351f95 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py @@ -616,7 +616,7 @@ def split_list(lst, n): k, m = divmod(len(lst), n) return [lst[i * k + min(i, m):(i + 1) * k + min(i + 1, m)] for i in range(n)] - attesting_tips = rnd.sample(block_tree_tips, min(len(block_tree_tips), MAX_TIPS_TO_ATTEST)) + attesting_tips = rnd.sample(sorted(block_tree_tips), min(len(block_tree_tips), MAX_TIPS_TO_ATTEST)) attesters = split_list([i for i in range(len(post_state.validators))], len(attesting_tips)) for index, attesting_block_index in enumerate(attesting_tips): # Advance the state to the current slot From 71e70adffb264975f15301ce5337531ea295a38d Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 2 Apr 2024 01:41:45 +0400 Subject: [PATCH 020/111] Added an option to pre-generated model instances: `--fc-gen-instances-path` --- .../generators/fork_choice_generated/main.py | 36 +++++++++++++++---- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/tests/generators/fork_choice_generated/main.py b/tests/generators/fork_choice_generated/main.py index f4fd4b66db..90c7959cf8 100644 --- a/tests/generators/fork_choice_generated/main.py +++ b/tests/generators/fork_choice_generated/main.py @@ -7,8 +7,13 @@ from eth2spec.utils import bls from eth2spec.test.helpers.typing import SpecForkName, PresetBaseName from minizinc import Instance, Model, Solver +from ruamel.yaml import YAML import random + +yaml = YAML(typ='safe') + + BLS_ACTIVE = False GENERATOR_NAME = 'fork_choice_generated' @@ -41,13 +46,18 @@ def _find_sm_link_solutions(anchor_epoch: int, yield [_ for _ in zip(solutions[i, 'sources'], solutions[i, 'targets'])] +def _load_sm_link_solutions(sm_links_path: str) -> Iterable[Iterable[tuple]]: + solutions = yaml.load(open(sm_links_path, 'r')) + print('solutions', solutions) + for solution in solutions: + yield list(zip(solution['sources'], solution['targets'])) + + def _create_providers(forks: Iterable[SpecForkName], presets: Iterable[PresetBaseName], debug: bool, initial_seed: int, - anchor_epoch: int, - number_of_epochs: int, - number_of_links: int, + solutions: Iterable[Iterable[tuple]], number_of_variations: int) -> Iterable[TestProvider]: def prepare_fn() -> None: bls.use_milagro() @@ -55,7 +65,7 @@ def prepare_fn() -> None: def make_cases_fn() -> Iterable[TestCase]: test_fn = _import_test_fn() - solutions = _find_sm_link_solutions(anchor_epoch, number_of_epochs, number_of_links) + # solutions = _find_sm_link_solutions(anchor_epoch, number_of_epochs, number_of_links) seeds = [initial_seed] if number_of_variations > 1: @@ -139,15 +149,27 @@ def make_cases_fn() -> Iterable[TestCase]: help='Number of super majority links per solution' ) + arg_parser.add_argument( + '--fc-gen-instances-path', + dest='fc_gen_instances_path', + default=None, + type=str, + required=False, + help='Path to a file with SM link instances' + ) + args = arg_parser.parse_args() + if args.fc_gen_instances_path is not None: + solutions = _load_sm_link_solutions(args.fc_gen_instances_path) + else: + solutions = _find_sm_link_solutions(args.fc_gen_anchor_epoch, args.fc_gen_epochs, args.fc_gen_links) + gen_runner.run_generator(GENERATOR_NAME, _create_providers(forks=forks, presets=presets, debug=args.fc_gen_debug, initial_seed=args.fc_gen_seed, - anchor_epoch=args.fc_gen_anchor_epoch, - number_of_epochs=args.fc_gen_epochs, - number_of_links=args.fc_gen_links, + solutions=solutions, number_of_variations=args.fc_gen_variations), arg_parser) From 0f358da4bc3149f403f474384c5e78612bb80d57 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 2 Apr 2024 01:46:12 +0400 Subject: [PATCH 021/111] Sample yaml file with pre-generated instances --- .../sample_block_tree.yaml | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 tests/generators/fork_choice_generated/sample_block_tree.yaml diff --git a/tests/generators/fork_choice_generated/sample_block_tree.yaml b/tests/generators/fork_choice_generated/sample_block_tree.yaml new file mode 100644 index 0000000000..488d597379 --- /dev/null +++ b/tests/generators/fork_choice_generated/sample_block_tree.yaml @@ -0,0 +1,26 @@ +- sources: [0] + targets: [1] +- sources: [0, 0, 0] + targets: [1, 2, 3] +- sources: [0, 0, 1] + targets: [1, 2, 3] +- sources: [0, 0, 2] + targets: [1, 2, 3] +- sources: [0, 0, 0, 0] + targets: [1, 2, 3, 4] +- sources: [0, 0, 0, 1] + targets: [1, 2, 3, 4] +- sources: [0, 0, 0, 2] + targets: [1, 2, 3, 4] +- sources: [0, 0, 0, 3] + targets: [1, 2, 3, 4] +- sources: [0, 0, 1, 1] + targets: [1, 2, 3, 4] +- sources: [0, 0, 1, 2] + targets: [1, 2, 3, 4] +- sources: [0, 0, 1, 3] + targets: [1, 2, 3, 4] +- sources: [0, 0, 2, 2] + targets: [1, 2, 3, 4] +- sources: [0, 0, 2, 3] + targets: [1, 2, 3, 4] From c819ef4b4207589a82ca39c44d0092f448a3dd3b Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 2 Apr 2024 01:57:22 +0400 Subject: [PATCH 022/111] minor fixes --- tests/generators/fork_choice_generated/main.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/generators/fork_choice_generated/main.py b/tests/generators/fork_choice_generated/main.py index 90c7959cf8..24ce7d6922 100644 --- a/tests/generators/fork_choice_generated/main.py +++ b/tests/generators/fork_choice_generated/main.py @@ -148,14 +148,13 @@ def make_cases_fn() -> Iterable[TestCase]: required=False, help='Number of super majority links per solution' ) - arg_parser.add_argument( '--fc-gen-instances-path', dest='fc_gen_instances_path', default=None, type=str, required=False, - help='Path to a file with SM link instances' + help='Path to a file with pre-generated instances' ) args = arg_parser.parse_args() From 9b5f6ec2e06504820464a20305851d6a199fb2ee Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 2 Apr 2024 02:26:40 +0400 Subject: [PATCH 023/111] rename params --- tests/generators/fork_choice_generated/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/generators/fork_choice_generated/main.py b/tests/generators/fork_choice_generated/main.py index 24ce7d6922..770586057b 100644 --- a/tests/generators/fork_choice_generated/main.py +++ b/tests/generators/fork_choice_generated/main.py @@ -46,8 +46,8 @@ def _find_sm_link_solutions(anchor_epoch: int, yield [_ for _ in zip(solutions[i, 'sources'], solutions[i, 'targets'])] -def _load_sm_link_solutions(sm_links_path: str) -> Iterable[Iterable[tuple]]: - solutions = yaml.load(open(sm_links_path, 'r')) +def _load_sm_link_solutions(instance_path: str) -> Iterable[Iterable[tuple]]: + solutions = yaml.load(open(instance_path, 'r')) print('solutions', solutions) for solution in solutions: yield list(zip(solution['sources'], solution['targets'])) From a0999cd1635b3ae6516354422f3fb3307c862d3a Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 2 Apr 2024 03:13:37 +0400 Subject: [PATCH 024/111] Modified `filter_block_tree_generator.py`: added an option to loa pre-generated MiniZinc instances. --- .../fork_choice_generated/block_cover.yaml | 162 ++++++++++++++++++ .../filter_block_tree_generator.py | 90 +++++++--- 2 files changed, 225 insertions(+), 27 deletions(-) create mode 100644 tests/generators/fork_choice_generated/block_cover.yaml diff --git a/tests/generators/fork_choice_generated/block_cover.yaml b/tests/generators/fork_choice_generated/block_cover.yaml new file mode 100644 index 0000000000..d04d3b1408 --- /dev/null +++ b/tests/generators/fork_choice_generated/block_cover.yaml @@ -0,0 +1,162 @@ +- block_epochs: [0] + current_epoch: 1 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: true} + previous_justifications: [false] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [0, 1] + current_epoch: 1 + current_justifications: [false, false] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: true} + previous_justifications: [false, false] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [0] + current_epoch: 3 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: true} + previous_justifications: [false] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [0, 1] + current_epoch: 3 + current_justifications: [false, false] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: true} + previous_justifications: [false, false] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [0, 1, 1] + current_epoch: 2 + current_justifications: [false, false, true] + parents: [0, 0, 0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false, false] + store_justified_epoch: 1 + target_block: 2 +- block_epochs: [0, 1, 1, 2, 2] + current_epoch: 2 + current_justifications: [false, true, false, false, false] + parents: [0, 0, 0, 1, 1] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false, false, false, false] + store_justified_epoch: 1 + target_block: 1 +- block_epochs: [0, 1, 1] + current_epoch: 2 + current_justifications: [false, false, true] + parents: [0, 0, 0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false, false] + store_justified_epoch: 1 + target_block: 1 +- block_epochs: [0, 1] + current_epoch: 2 + current_justifications: [false, true] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false] + store_justified_epoch: 1 + target_block: 0 +- block_epochs: [0, 1, 1] + current_epoch: 3 + current_justifications: [false, false, true] + parents: [0, 0, 0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, false, false] + store_justified_epoch: 1 + target_block: 1 +- block_epochs: [0, 1] + current_epoch: 3 + current_justifications: [false, true] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, false] + store_justified_epoch: 1 + target_block: 0 +- block_epochs: [2] + current_epoch: 3 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2, 3] + current_epoch: 3 + current_justifications: [false, false] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2] + current_epoch: 5 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2, 3] + current_epoch: 5 + current_justifications: [false, false] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2, 3, 3] + current_epoch: 4 + current_justifications: [false, false, true] + parents: [0, 0, 0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false, false] + store_justified_epoch: 3 + target_block: 1 +- block_epochs: [2, 3] + current_epoch: 4 + current_justifications: [false, true] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false] + store_justified_epoch: 3 + target_block: 0 +- block_epochs: [2, 3, 3] + current_epoch: 5 + current_justifications: [false, false, true] + parents: [0, 0, 0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, false, false] + store_justified_epoch: 3 + target_block: 1 +- block_epochs: [2, 3] + current_epoch: 5 + current_justifications: [false, true] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, false] + store_justified_epoch: 3 + target_block: 0 diff --git a/tests/generators/fork_choice_generated/filter_block_tree_generator.py b/tests/generators/fork_choice_generated/filter_block_tree_generator.py index 06e3145993..7b0c62f48b 100644 --- a/tests/generators/fork_choice_generated/filter_block_tree_generator.py +++ b/tests/generators/fork_choice_generated/filter_block_tree_generator.py @@ -8,11 +8,20 @@ from eth2spec.test.helpers.typing import SpecForkName, PresetBaseName from minizinc import Instance, Model, Solver import random +from ruamel.yaml import YAML + BLS_ACTIVE = False GENERATOR_NAME = 'filter_block_tree' +def _load_model_solutions(instance_path: str): + yaml = YAML(typ='safe') + solutions = yaml.load(open(instance_path, 'r')) + print('solutions', solutions) + return solutions + + def _import_test_fn(): src = import_module('eth2spec.test.phase0.fork_choice.test_sm_links_tree_model') print("generating test vectors from tests source: %s" % src.__name__) @@ -23,7 +32,8 @@ def _find_model_solutions(anchor_epoch: int, store_justified_epoch_equal_zero: bool, block_voting_source_epoch_equal_store_justified_epoch: bool, block_voting_source_epoch_plus_two_greater_or_equal_current_epoch: bool, - block_is_leaf: bool) -> []: + block_is_leaf: bool, + nr_solutions: int=5) -> []: block_cover3 = Model('./model/minizinc/Block_cover3.mzn') solver = Solver.lookup("gecode") instance = Instance(solver, block_cover3) @@ -33,7 +43,7 @@ def _find_model_solutions(anchor_epoch: int, instance['block_vse_plus_two_ge_curr_e'] = block_voting_source_epoch_plus_two_greater_or_equal_current_epoch instance['block_is_leaf'] = block_is_leaf - result = instance.solve(nr_solutions=5) + result = instance.solve(nr_solutions=nr_solutions) output = [] for s in result.solution: @@ -55,11 +65,39 @@ def _find_model_solutions(anchor_epoch: int, return output +def _generate_model_solutions(anchor_epoch: int, nr_solutions: int=5): + solutions = [] + + for store_je_eq_zero in [True, False]: + for block_vse_eq_store_je in [True, False]: + for block_vse_plus_two_ge_curr_e in [True, False]: + for block_is_leaf in [True, False]: + if store_je_eq_zero and not block_vse_eq_store_je: + continue + results = _find_model_solutions(anchor_epoch=0 if store_je_eq_zero else anchor_epoch, + store_justified_epoch_equal_zero=store_je_eq_zero, + block_voting_source_epoch_equal_store_justified_epoch=block_vse_eq_store_je, + block_voting_source_epoch_plus_two_greater_or_equal_current_epoch=block_vse_plus_two_ge_curr_e, + block_is_leaf=block_is_leaf, + nr_solutions=nr_solutions) + print('\n\n') + print(['store_je_eq_zero=' + str(store_je_eq_zero), + 'block_vse_eq_store_je=' + str(block_vse_eq_store_je), + 'block_vse_plus_two_ge_curr_e=' + str(block_vse_plus_two_ge_curr_e), + 'block_is_leaf=' + str(block_is_leaf)]) + for r in results: + print(r) + + solutions.extend(results) + + return solutions + + def _create_providers(forks: Iterable[SpecForkName], presets: Iterable[PresetBaseName], debug: bool, initial_seed: int, - anchor_epoch: int, + solutions, number_of_variations: int) -> Iterable[TestProvider]: def prepare_fn() -> None: bls.use_milagro() @@ -74,29 +112,6 @@ def make_cases_fn() -> Iterable[TestCase]: seeds = [rnd.randint(1, 10000) for _ in range(number_of_variations)] seeds[0] = initial_seed - solutions = [] - - for store_je_eq_zero in [True, False]: - for block_vse_eq_store_je in [True, False]: - for block_vse_plus_two_ge_curr_e in [True, False]: - for block_is_leaf in [True, False]: - if store_je_eq_zero and not block_vse_eq_store_je: - continue - results = _find_model_solutions(anchor_epoch=0 if store_je_eq_zero else anchor_epoch, - store_justified_epoch_equal_zero=store_je_eq_zero, - block_voting_source_epoch_equal_store_justified_epoch=block_vse_eq_store_je, - block_voting_source_epoch_plus_two_greater_or_equal_current_epoch=block_vse_plus_two_ge_curr_e, - block_is_leaf=block_is_leaf) - print('\n\n') - print(['store_je_eq_zero=' + str(store_je_eq_zero), - 'block_vse_eq_store_je=' + str(block_vse_eq_store_je), - 'block_vse_plus_two_ge_curr_e=' + str(block_vse_plus_two_ge_curr_e), - 'block_is_leaf=' + str(block_is_leaf)]) - for r in results: - print(r) - - solutions = solutions + results - for i, solution in enumerate(solutions): for seed in seeds: for fork_name in forks: @@ -156,14 +171,35 @@ def make_cases_fn() -> Iterable[TestCase]: required=False, help='Anchor epoch' ) + arg_parser.add_argument( + '--fc-gen-nr-solutions', + dest='fc_gen_nr_solutions', + default=5, + type=int, + required=False, + help='Number of solutions per MiniZinc query' + ) + arg_parser.add_argument( + '--fc-gen-instances-path', + dest='fc_gen_instances_path', + default=None, + type=str, + required=False, + help='Path to a file with pre-generated instances' + ) args = arg_parser.parse_args() + if args.fc_gen_instances_path is not None: + solutions = _load_model_solutions(args.fc_gen_instances_path) + else: + solutions = _generate_model_solutions(args.fc_gen_anchor_epoch, args.fc_gen_nr_solutions) + gen_runner.run_generator(GENERATOR_NAME, _create_providers(forks=forks, presets=presets, debug=args.fc_gen_debug, initial_seed=args.fc_gen_seed, - anchor_epoch=args.fc_gen_anchor_epoch, + solutions=solutions, number_of_variations=args.fc_gen_variations), arg_parser) From 5f00017383732eb5e39172de1ee7d48643df2033 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Sat, 6 Apr 2024 17:59:10 +0600 Subject: [PATCH 025/111] Implement on_attestation --- .../fork_choice/test_sm_links_tree_model.py | 78 ++++++++++++++----- 1 file changed, 59 insertions(+), 19 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py index fff3351f95..fd613f7e1d 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py @@ -24,6 +24,7 @@ get_genesis_forkchoice_store_and_block, on_tick_and_append_step, tick_and_add_block, + add_attestation, ) from eth2spec.test.helpers.state import ( state_transition_and_sign_block, @@ -46,6 +47,8 @@ EMPTY_SLOTS_RATE = 3 MAX_TIPS_TO_ATTEST = 2 +OUT_OF_BLOCK_ATTESTATION_RATE = 10 +IN_AND_OUT_OF_BLOCK_ATTESTATION_RATE = 90 class SmLink(tuple): @property @@ -578,14 +581,15 @@ def _generate_sm_link_tree(spec, genesis_state, sm_links, rnd: random.Random, de return sorted(signed_blocks, key=lambda b: b.message.slot), branch_tips[highest_target_sm_link] -def _generate_block_tree(spec, anchor_tip: BranchTip, rnd: random.Random, debug, block_parents) -> []: +def _generate_block_tree(spec, anchor_tip: BranchTip, rnd: random.Random, debug, block_parents) -> ([], []): signed_blocks = [] - attestations = anchor_tip.attestations.copy() + in_block_attestations = anchor_tip.attestations.copy() post_states = [anchor_tip.beacon_state.copy()] current_slot = anchor_tip.beacon_state.slot block_index = 1 block_tree_tips = set([0]) + out_of_block_attestations = [] while block_index < len(block_parents): # Propose a block if slot shouldn't be empty @@ -596,11 +600,12 @@ def _generate_block_tree(spec, anchor_tip: BranchTip, rnd: random.Random, debug, transition_to(spec, parent_state, current_slot) # Filter out non-viable attestations - attestations = [a for a in attestations if parent_state.slot <= a.data.slot + spec.SLOTS_PER_EPOCH] + in_block_attestations = [a for a in in_block_attestations + if parent_state.slot <= a.data.slot + spec.SLOTS_PER_EPOCH] # Produce block proposer = spec.get_beacon_proposer_index(parent_state) - signed_block, post_state, attestations = _produce_block(spec, parent_state, attestations) + signed_block, post_state, in_block_attestations = _produce_block(spec, parent_state, in_block_attestations) signed_blocks.append(signed_block) post_states.append(post_state) @@ -617,7 +622,8 @@ def split_list(lst, n): return [lst[i * k + min(i, m):(i + 1) * k + min(i + 1, m)] for i in range(n)] attesting_tips = rnd.sample(sorted(block_tree_tips), min(len(block_tree_tips), MAX_TIPS_TO_ATTEST)) - attesters = split_list([i for i in range(len(post_state.validators))], len(attesting_tips)) + validator_count = len(post_states[attesting_tips[0]].validators) + attesters = split_list([i for i in range(validator_count)], len(attesting_tips)) for index, attesting_block_index in enumerate(attesting_tips): # Advance the state to the current slot attesting_state = post_states[attesting_block_index] @@ -626,8 +632,19 @@ def split_list(lst, n): # Attest to the block attestations_in_slot = _attest_to_slot(spec, attesting_state, attesting_state.slot, lambda comm: [i for i in comm if i in attesters[index]]) - # Prepend attestations to the list - attestations = list(attestations_in_slot) + attestations + + # Sample strictly out of block attestations + new_out_of_block_attestations = rnd.sample( + attestations_in_slot, len(attestations_in_slot) * OUT_OF_BLOCK_ATTESTATION_RATE // 100) + + # Prepend in block attestations to the list + new_in_block_attestations = [a for a in attestations_in_slot if a not in new_out_of_block_attestations] + in_block_attestations = new_in_block_attestations + in_block_attestations + + # Sample out of block attestations that are also included in block + new_in_and_out_of_block_attestations = rnd.sample( + new_in_block_attestations, len(new_in_block_attestations) * IN_AND_OUT_OF_BLOCK_ATTESTATION_RATE // 100) + out_of_block_attestations += new_out_of_block_attestations + new_in_and_out_of_block_attestations # Next slot current_slot += 1 @@ -638,8 +655,11 @@ def split_list(lst, n): print(' ', 'state.current_justified_checkpoint:', '(epoch=' + str(post_states[len(post_states) - 1].current_justified_checkpoint.epoch) + ', root=' + str(post_states[len(post_states) - 1].current_justified_checkpoint.root)[:6] + ')') + print('on_attestation: ') + print(' ', 'count =', len(out_of_block_attestations)) - return sorted(signed_blocks, key=lambda b: b.message.slot) + return (sorted(signed_blocks, key=lambda b: b.message.slot), + sorted(out_of_block_attestations, key=lambda a: a.data.slot)) def _print_head(spec, store): @@ -652,6 +672,11 @@ def _print_head(spec, store): weight * 100 // total_active_balance) + '%)' +def _on_tick_and_append_step(spec, store, slot, test_steps): + time = slot * spec.config.SECONDS_PER_SLOT + store.genesis_time + on_tick_and_append_step(spec, store, time, test_steps) + assert store.time == time + @with_altair_and_later @spec_state_test def test_sm_links_tree_model(spec, state, debug=False, seed=1, sm_links=None): @@ -687,8 +712,9 @@ def test_sm_links_tree_model(spec, state, debug=False, seed=1, sm_links=None): seed = new_seed # Block tree model + attestations = [] if block_parents is not None: - block_tree = _generate_block_tree(spec, highest_tip, rnd, debug, block_parents) + block_tree, attestations = _generate_block_tree(spec, highest_tip, rnd, debug, block_parents) # Merge block_tree and sm_link_tree blocks block_tree_root_slot = block_tree[0].message.slot signed_blocks = [sb for sb in signed_blocks if sb.message.slot < block_tree_root_slot] @@ -703,16 +729,30 @@ def test_sm_links_tree_model(spec, state, debug=False, seed=1, sm_links=None): store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) yield 'anchor_state', state yield 'anchor_block', anchor_block - current_time = state.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time - on_tick_and_append_step(spec, store, current_time, test_steps) - assert store.time == current_time - - # Apply generated blocks - for signed_block in signed_blocks: - block = signed_block.message - yield from tick_and_add_block(spec, store, signed_block, test_steps) - block_root = block.hash_tree_root() - assert store.blocks[block_root] == block + _on_tick_and_append_step(spec, store, state.slot, test_steps) + + # Apply generated messages + start_slot = min(b.message.slot for b in signed_blocks) + end_slot = max(max(b.message.slot for b in signed_blocks), + max(a.data.slot for a in attestations)) + 1 + + # Advance time to start_slot + _on_tick_and_append_step(spec, store, start_slot, test_steps) + + # Apply messages to store + for slot in range(start_slot, end_slot + 1): + # on_tick + _on_tick_and_append_step(spec, store, slot, test_steps) + + # on_attestation for attestations from the previous slot + for attestation in (a for a in attestations if a.data.slot == slot - 1): + yield from add_attestation(spec, store, attestation, test_steps) + + # on_block for blocks from the current slot + for block in (b for b in signed_blocks if b.message.slot == slot): + yield from tick_and_add_block(spec, store, block, test_steps) + block_root = block.message.hash_tree_root() + assert store.blocks[block_root] == block.message if debug: print(' head: ' + _print_head(spec, store)) From 310893cb79269b4af723c1120926eb8f36d097b7 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Sun, 7 Apr 2024 19:36:24 +0600 Subject: [PATCH 026/111] Implement attester slashings --- .../fork_choice/test_sm_links_tree_model.py | 84 +++++++++++++++---- .../generators/fork_choice_generated/main.py | 9 +- 2 files changed, 76 insertions(+), 17 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py index fd613f7e1d..004a2e4a27 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py @@ -20,11 +20,15 @@ get_valid_attestation_at_slot, get_valid_attestation, ) +from eth2spec.test.helpers.attester_slashings import ( + get_valid_attester_slashing_by_indices, +) from eth2spec.test.helpers.fork_choice import ( get_genesis_forkchoice_store_and_block, on_tick_and_append_step, tick_and_add_block, add_attestation, + add_attester_slashing, ) from eth2spec.test.helpers.state import ( state_transition_and_sign_block, @@ -50,6 +54,10 @@ OUT_OF_BLOCK_ATTESTATION_RATE = 10 IN_AND_OUT_OF_BLOCK_ATTESTATION_RATE = 90 +MAX_ATTESTER_SLASHINGS = 8 +ATTESTER_SLASHINGS_RATE = 8 +OFF_AND_ON_CHAIN_SLASHING_RATES = [33, 66] # Off chain, on chain + class SmLink(tuple): @property def source(self): @@ -194,7 +202,7 @@ def _get_eligible_attestations(spec, state, attestations) -> []: return [a for a in attestations if state.slot <= a.data.slot + spec.SLOTS_PER_EPOCH] -def _produce_block(spec, state, attestations): +def _produce_block(spec, state, attestations, attester_slashings=[]): """ Produces a block including as many attestations as it is possible. :return: Signed block, the post block state and attestations that were not included into the block. @@ -211,6 +219,11 @@ def _produce_block(spec, state, attestations): for a in attestation_in_block: block.body.attestations.append(a) + # Add attester slashings + attester_slashings_in_block = attester_slashings[:spec.MAX_ATTESTER_SLASHINGS] + for s in attester_slashings_in_block: + block.body.attester_slashings.append(s) + # Run state transition and sign off on a block post_state = state.copy() spec.process_block(post_state, block) @@ -218,8 +231,9 @@ def _produce_block(spec, state, attestations): signed_block = sign_block(spec, post_state, block) not_included_attestations = [a for a in attestations if a not in attestation_in_block] + not_included_attester_slashings = [s for s in attester_slashings if s not in attester_slashings_in_block] - return signed_block, post_state, not_included_attestations + return signed_block, post_state, not_included_attestations, not_included_attester_slashings def _attest_to_slot(spec, state, slot_to_attest, participants_filter=None) -> []: @@ -287,7 +301,7 @@ def participants_filter(comm): # Produce block if the proposer is among participanting validators proposer = spec.get_beacon_proposer_index(state) if state.slot > spec.GENESIS_SLOT and proposer in branch_tip.participants: - signed_block, state, attestations = _produce_block(spec, state, attestations) + signed_block, state, attestations, _ = _produce_block(spec, state, attestations) signed_blocks.append(signed_block) if enable_attesting: @@ -531,7 +545,7 @@ def _generate_sm_link_tree(spec, genesis_state, sm_links, rnd: random.Random, de while (spec.get_beacon_proposer_index(state) not in advanced_branch_tip.participants): next_slot(spec, state) - tip_block, _, _ = _produce_block(spec, state, []) + tip_block, _, _, _ = _produce_block(spec, state, []) new_signed_blocks.append(tip_block) assert state.current_justified_checkpoint.epoch == sm_link.target, \ @@ -581,7 +595,12 @@ def _generate_sm_link_tree(spec, genesis_state, sm_links, rnd: random.Random, de return sorted(signed_blocks, key=lambda b: b.message.slot), branch_tips[highest_target_sm_link] -def _generate_block_tree(spec, anchor_tip: BranchTip, rnd: random.Random, debug, block_parents) -> ([], []): +def _generate_block_tree(spec, + anchor_tip: BranchTip, + rnd: random.Random, + debug, + block_parents, + with_attester_slashings) -> ([], [], []): signed_blocks = [] in_block_attestations = anchor_tip.attestations.copy() @@ -590,6 +609,9 @@ def _generate_block_tree(spec, anchor_tip: BranchTip, rnd: random.Random, debug, block_index = 1 block_tree_tips = set([0]) out_of_block_attestations = [] + in_block_attester_slashings = [] + out_of_block_attester_slashings = [] + attester_slashings_count = 0 while block_index < len(block_parents): # Propose a block if slot shouldn't be empty @@ -605,7 +627,8 @@ def _generate_block_tree(spec, anchor_tip: BranchTip, rnd: random.Random, debug, # Produce block proposer = spec.get_beacon_proposer_index(parent_state) - signed_block, post_state, in_block_attestations = _produce_block(spec, parent_state, in_block_attestations) + signed_block, post_state, in_block_attestations, in_block_attester_slashings = _produce_block( + spec, parent_state, in_block_attestations, in_block_attester_slashings) signed_blocks.append(signed_block) post_states.append(post_state) @@ -646,6 +669,27 @@ def split_list(lst, n): new_in_block_attestations, len(new_in_block_attestations) * IN_AND_OUT_OF_BLOCK_ATTESTATION_RATE // 100) out_of_block_attestations += new_out_of_block_attestations + new_in_and_out_of_block_attestations + # Create attester slashing + if with_attester_slashings and attester_slashings_count < MAX_ATTESTER_SLASHINGS: + if rnd.randint(0, 99) < ATTESTER_SLASHINGS_RATE: + state = post_states[attesting_tips[0]] + indices = [rnd.randint(0, len(state.validators) - 1)] + attester_slashing = get_valid_attester_slashing_by_indices(spec, state, indices, + slot=current_slot, + signed_1=True, + signed_2=True) + + choice = rnd.randint(0, 99) + if choice < OFF_AND_ON_CHAIN_SLASHING_RATES[0]: + out_of_block_attester_slashings.append(attester_slashing) + elif choice < OFF_AND_ON_CHAIN_SLASHING_RATES[1]: + in_block_attester_slashings.append(attester_slashing) + else: + out_of_block_attester_slashings.append(attester_slashing) + in_block_attester_slashings.append(attester_slashing) + + attester_slashings_count += 1 + # Next slot current_slot += 1 @@ -657,9 +701,12 @@ def split_list(lst, n): ', root=' + str(post_states[len(post_states) - 1].current_justified_checkpoint.root)[:6] + ')') print('on_attestation: ') print(' ', 'count =', len(out_of_block_attestations)) + print('on_attester_slashing: ') + print(' ', 'count =', len(out_of_block_attester_slashings)) return (sorted(signed_blocks, key=lambda b: b.message.slot), - sorted(out_of_block_attestations, key=lambda a: a.data.slot)) + sorted(out_of_block_attestations, key=lambda a: a.data.slot), + sorted(out_of_block_attester_slashings, key=lambda a: a.attestation_1.data.slot)) def _print_head(spec, store): @@ -679,7 +726,7 @@ def _on_tick_and_append_step(spec, store, slot, test_steps): @with_altair_and_later @spec_state_test -def test_sm_links_tree_model(spec, state, debug=False, seed=1, sm_links=None): +def test_sm_links_tree_model(spec, state, debug=False, seed=1, sm_links=None, with_attester_slashings=False): block_parents = [0, 0, 0, 2, 2, 1, 1, 6, 6, 5, 5, 4, 7, 4, 3, 3] # This test is mainly used for the test generation purposes @@ -713,8 +760,10 @@ def test_sm_links_tree_model(spec, state, debug=False, seed=1, sm_links=None): # Block tree model attestations = [] + attester_slashings = [] if block_parents is not None: - block_tree, attestations = _generate_block_tree(spec, highest_tip, rnd, debug, block_parents) + block_tree, attestations, attester_slashings = _generate_block_tree( + spec, highest_tip, rnd, debug, block_parents, with_attester_slashings) # Merge block_tree and sm_link_tree blocks block_tree_root_slot = block_tree[0].message.slot signed_blocks = [sb for sb in signed_blocks if sb.message.slot < block_tree_root_slot] @@ -732,9 +781,12 @@ def test_sm_links_tree_model(spec, state, debug=False, seed=1, sm_links=None): _on_tick_and_append_step(spec, store, state.slot, test_steps) # Apply generated messages + max_block_slot = max(b.message.slot for b in signed_blocks) + max_attestation_slot = max(a.data.slot for a in attestations) if any(attestations) else 0 + max_slashing_slot = max(s.attestation_1.data.slot for s in attester_slashings) if any(attester_slashings) else 0 + start_slot = min(b.message.slot for b in signed_blocks) - end_slot = max(max(b.message.slot for b in signed_blocks), - max(a.data.slot for a in attestations)) + 1 + end_slot = max(max_block_slot, max_attestation_slot, max_slashing_slot) + 1 # Advance time to start_slot _on_tick_and_append_step(spec, store, start_slot, test_steps) @@ -748,6 +800,10 @@ def test_sm_links_tree_model(spec, state, debug=False, seed=1, sm_links=None): for attestation in (a for a in attestations if a.data.slot == slot - 1): yield from add_attestation(spec, store, attestation, test_steps) + # on_attester_slashing for slashing from the previous slot + for attester_slashing in (s for s in attester_slashings if s.attestation_1.data.slot == slot - 1): + yield from add_attester_slashing(spec, store, attester_slashing, test_steps) + # on_block for blocks from the current slot for block in (b for b in signed_blocks if b.message.slot == slot): yield from tick_and_add_block(spec, store, block, test_steps) @@ -797,10 +853,10 @@ def _generate_filter_block_tree(spec, genesis_state, block_epochs, parents, prev while (state.slot < threshold_slot): # Do not include attestations into blocks if (state.slot < spec.compute_start_slot_at_epoch(epoch)): - new_block, state, _ = _produce_block(spec, state, []) + new_block, state, _, _ = _produce_block(spec, state, []) signed_blocks.append(new_block) else: - new_block, state, attestations = _produce_block(spec, state, attestations) + new_block, state, attestations, _ = _produce_block(spec, state, attestations) signed_blocks.append(new_block) # Attest @@ -843,7 +899,7 @@ def _generate_filter_block_tree(spec, genesis_state, block_epochs, parents, prev block_attestations = block_attestations + current_epoch_attestations # Propose block - new_block, state, _ = _produce_block(spec, state, block_attestations) + new_block, state, _, _ = _produce_block(spec, state, block_attestations) signed_blocks.append(new_block) # Attest diff --git a/tests/generators/fork_choice_generated/main.py b/tests/generators/fork_choice_generated/main.py index 770586057b..5ccc7344a3 100644 --- a/tests/generators/fork_choice_generated/main.py +++ b/tests/generators/fork_choice_generated/main.py @@ -58,7 +58,8 @@ def _create_providers(forks: Iterable[SpecForkName], debug: bool, initial_seed: int, solutions: Iterable[Iterable[tuple]], - number_of_variations: int) -> Iterable[TestProvider]: + number_of_variations: int, + with_attester_slashings: bool) -> Iterable[TestProvider]: def prepare_fn() -> None: bls.use_milagro() return @@ -89,7 +90,8 @@ def make_cases_fn() -> Iterable[TestCase]: bls_active=BLS_ACTIVE, debug=debug, seed=seed, - sm_links=solution)) + sm_links=solution, + with_attester_slashings=with_attester_slashings)) yield TestProvider(prepare=prepare_fn, make_cases=make_cases_fn) @@ -170,5 +172,6 @@ def make_cases_fn() -> Iterable[TestCase]: debug=args.fc_gen_debug, initial_seed=args.fc_gen_seed, solutions=solutions, - number_of_variations=args.fc_gen_variations), + number_of_variations=args.fc_gen_variations, + with_attester_slashings=True), arg_parser) From cc852cac303083961456137f7fb918af22b5f07a Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Mon, 8 Apr 2024 19:16:56 +0600 Subject: [PATCH 027/111] Update block tree model --- .../model/minizinc/Block_tree.mzn | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/tests/generators/fork_choice_generated/model/minizinc/Block_tree.mzn b/tests/generators/fork_choice_generated/model/minizinc/Block_tree.mzn index 09bea558d5..6a3993c99c 100644 --- a/tests/generators/fork_choice_generated/model/minizinc/Block_tree.mzn +++ b/tests/generators/fork_choice_generated/model/minizinc/Block_tree.mzn @@ -1,11 +1,16 @@ include "globals.mzn"; -set of int: BLOCKS = 0..7; +int: NB; % num of blocks +int: MD; % max depth +% int: MW; % max width +int: MC; % max children + +set of int: BLOCKS = 0..(NB-1); array[BLOCKS] of var BLOCKS: parent; -array[BLOCKS] of var set of BLOCKS: children; +% array[BLOCKS] of var set of BLOCKS: children; array[BLOCKS] of var int: depths; -array[BLOCKS] of var int: widths; +% array[BLOCKS] of var int: widths; function var int: count_children(var BLOCKS: block) = sum(ch in BLOCKS)(if ch != 0 /\ parent[ch] == block then 1 else 0 endif); @@ -18,15 +23,16 @@ function var int: sub_tree_size(var BLOCKS: block) = endif; -array[BLOCKS] of var int: tst; -array[BLOCKS] of var int: tst2; +% array[BLOCKS] of var int: tst; +% array[BLOCKS] of var int: tst2; -constraint forall(b in BLOCKS)(tst[b] == count_children(b)); -constraint forall(b in BLOCKS)(tst2[b] == card(children[b])); -constraint int_set_channel(parent, children); +% constraint forall(b in BLOCKS)(tst[b] == count_children(b)); +% constraint forall(b in BLOCKS)(tst2[b] == card(children[b])); +% constraint int_set_channel(parent, children); constraint forall(b in BLOCKS)(if b == 0 then depths[b] == 0 else depths[b] == depths[parent[b]] + 1 endif); constraint forall(b in BLOCKS)(if b != 0 then parent[b] < b else parent[b] == b endif); -constraint forall(b in BLOCKS)(count_children(b) <= 2); -constraint forall(b in BLOCKS)(depths[b] <= 5); -constraint forall(b in BLOCKS)(widths[b] == sub_tree_size(b)); \ No newline at end of file +constraint forall(b in BLOCKS)(count_children(b) <= MC); +constraint forall(b in BLOCKS)(depths[b] <= MD); +% constraint forall(b in BLOCKS)(widths[b] == sub_tree_size(b)); +% constraint forall(b in BLOCKS)(widths[b] <= MW); \ No newline at end of file From d477b337fe1f6016525c5a536907d4693cc3d0da Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Wed, 3 Apr 2024 02:18:29 +0400 Subject: [PATCH 028/111] Added `test_gen.py` driver to generate all tests from a single confi. NB: to be renamed to `main.py` in future. --- .../filter_block_tree_generator.py | 7 +- .../generators/fork_choice_generated/main.py | 7 +- .../fork_choice_generated/test_gen.py | 86 +++++++++++++++++++ 3 files changed, 94 insertions(+), 6 deletions(-) create mode 100644 tests/generators/fork_choice_generated/test_gen.py diff --git a/tests/generators/fork_choice_generated/filter_block_tree_generator.py b/tests/generators/fork_choice_generated/filter_block_tree_generator.py index 7b0c62f48b..a1ffab1815 100644 --- a/tests/generators/fork_choice_generated/filter_block_tree_generator.py +++ b/tests/generators/fork_choice_generated/filter_block_tree_generator.py @@ -15,6 +15,10 @@ GENERATOR_NAME = 'filter_block_tree' +forks = [ALTAIR] +presets = [MINIMAL] + + def _load_model_solutions(instance_path: str): yaml = YAML(typ='safe') solutions = yaml.load(open(instance_path, 'r')) @@ -134,9 +138,6 @@ def make_cases_fn() -> Iterable[TestCase]: if __name__ == "__main__": - forks = [ALTAIR] - presets = [MINIMAL] - arg_parser = gen_runner.create_arg_parser(GENERATOR_NAME) arg_parser.add_argument( diff --git a/tests/generators/fork_choice_generated/main.py b/tests/generators/fork_choice_generated/main.py index 5ccc7344a3..00f8f8a4cc 100644 --- a/tests/generators/fork_choice_generated/main.py +++ b/tests/generators/fork_choice_generated/main.py @@ -18,6 +18,10 @@ GENERATOR_NAME = 'fork_choice_generated' +forks = [ALTAIR] +presets = [MINIMAL] + + def _import_test_fn(): src = import_module('eth2spec.test.phase0.fork_choice.test_sm_links_tree_model') print("generating test vectors from tests source: %s" % src.__name__) @@ -97,9 +101,6 @@ def make_cases_fn() -> Iterable[TestCase]: if __name__ == "__main__": - forks = [ALTAIR] - presets = [MINIMAL] - arg_parser = gen_runner.create_arg_parser(GENERATOR_NAME) arg_parser.add_argument( diff --git a/tests/generators/fork_choice_generated/test_gen.py b/tests/generators/fork_choice_generated/test_gen.py new file mode 100644 index 0000000000..6815025bf9 --- /dev/null +++ b/tests/generators/fork_choice_generated/test_gen.py @@ -0,0 +1,86 @@ +from eth2spec.gen_helpers.gen_base import gen_runner + +from filter_block_tree_generator import ( + forks as block_cover_forks, + presets as block_cover_presets, + _load_model_solutions as block_cover_load_solutions, + _create_providers as block_cover_create_providers +) +from main import ( + forks as block_tree_forks, + presets as block_tree_presets, + _load_sm_link_solutions as block_tree_load_solutions, + _create_providers as block_tree_create_providers +) + + +GENERATOR_NAME = 'fork_choice_generated' + + +test_gen_config = { + 'block_tree_test': { + 'test_type': 'block_tree', + 'instances': 'block_tree.yaml', + 'seed': 123, + 'nr_variations': 3, + }, + 'block_cover_test': { + 'test_type': 'block_cover', + 'instances': 'block_cover.yaml', + 'seed': 456, + 'nr_variations': 3, + } +} + +if __name__ == "__main__": + arg_parser = gen_runner.create_arg_parser(GENERATOR_NAME) + + arg_parser.add_argument( + '--fc-gen-debug', + dest='fc_gen_debug', + action='store_true', + default=False, + required=False, + help='If set provides debug output and enable additional checks for generated chains', + ) + # TODO: load test_gen_config from a yaml file + # arg_parser.add_argument( + # '--fc-gen-config', + # dest='fc_gen_config', + # type=str, + # default=None, + # required=False, + # help='Path to a file with test generator configurations' + # ) + + args = arg_parser.parse_args() + + for test_name, params in test_gen_config.items(): + print(test_name) + test_type = params['test_type'] + instances_path = params['instances'] + initial_seed = params['seed'] + nr_variations = params['nr_variations'] + + if test_type == 'block_tree': + solutions = block_tree_load_solutions(instances_path) + providers = block_tree_create_providers(forks=block_tree_forks, + presets=block_tree_presets, + debug=args.fc_gen_debug, + initial_seed=initial_seed, + solutions=solutions, + number_of_variations=nr_variations) + elif test_type == 'block_cover': + solutions = block_cover_load_solutions(instances_path) + providers = block_cover_create_providers(forks=block_cover_forks, + presets=block_cover_presets, + debug=args.fc_gen_debug, + initial_seed=initial_seed, + solutions=solutions, + number_of_variations=nr_variations) + else: + raise ValueError(f'Unsupported test type: {test_type}') + + gen_runner.run_generator(GENERATOR_NAME, providers, arg_parser) + + From b0be1aeb8ac030c208df28da094f2580cf8acf76 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Mon, 8 Apr 2024 22:34:10 +0600 Subject: [PATCH 029/111] Implement invalid blocks --- .../eth2spec/test/helpers/fork_choice.py | 9 +- .../fork_choice/test_sm_links_tree_model.py | 198 ++++++++++++------ .../generators/fork_choice_generated/main.py | 9 +- .../fork_choice_generated/test_gen.py | 4 +- 4 files changed, 145 insertions(+), 75 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py index 094e2e8a5c..48e5b579fa 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py +++ b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py @@ -80,10 +80,13 @@ def run_func(): yield from with_blob_data(spec, blob_data, run_func) -def add_attestation(spec, store, attestation, test_steps, is_from_block=False): - spec.on_attestation(store, attestation, is_from_block=is_from_block) +def add_attestation(spec, store, attestation, test_steps, is_from_block=False, valid=True): + run_on_attestation(spec, store, attestation, is_from_block=is_from_block, valid=valid) yield get_attestation_file_name(attestation), attestation - test_steps.append({'attestation': get_attestation_file_name(attestation)}) + test_steps.append({ + 'attestation': get_attestation_file_name(attestation), + 'valid': valid, + }) def add_attestations(spec, store, attestations, test_steps, is_from_block=False): diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py index 004a2e4a27..581507d665 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py @@ -29,6 +29,7 @@ tick_and_add_block, add_attestation, add_attester_slashing, + add_block, ) from eth2spec.test.helpers.state import ( state_transition_and_sign_block, @@ -51,12 +52,15 @@ EMPTY_SLOTS_RATE = 3 MAX_TIPS_TO_ATTEST = 2 -OUT_OF_BLOCK_ATTESTATION_RATE = 10 -IN_AND_OUT_OF_BLOCK_ATTESTATION_RATE = 90 +OFF_CHAIN_ATTESTATION_RATE = 10 +ON_CHAIN_ATTESTATION_RATE = 20 MAX_ATTESTER_SLASHINGS = 8 ATTESTER_SLASHINGS_RATE = 8 -OFF_AND_ON_CHAIN_SLASHING_RATES = [33, 66] # Off chain, on chain +OFF_CHAIN_SLASHING_RATE = 33 +ON_CHAIN_SLASHING_RATE = 33 + +INVALID_MESSAGES_RATE = 5 class SmLink(tuple): @property @@ -82,6 +86,12 @@ def copy(self): self.eventually_justified_checkpoint) +class ProtocolMessage: + def __init__(self, payload, valid=True): + self.payload = payload + self.valid = valid + + def _justifying_participation_rate(rnd: random.Random): """ Should be high enough to ensure justification happens @@ -226,12 +236,26 @@ def _produce_block(spec, state, attestations, attester_slashings=[]): # Run state transition and sign off on a block post_state = state.copy() - spec.process_block(post_state, block) + + valid = True + try: + spec.process_block(post_state, block) + except AssertionError: + valid = False + block.state_root = post_state.hash_tree_root() signed_block = sign_block(spec, post_state, block) - not_included_attestations = [a for a in attestations if a not in attestation_in_block] - not_included_attester_slashings = [s for s in attester_slashings if s not in attester_slashings_in_block] + # Filter out operations only if the block is valid + not_included_attestations = attestations + not_included_attester_slashings = attester_slashings + if valid: + not_included_attestations = [a for a in attestations if a not in attestation_in_block] + not_included_attester_slashings = [s for s in attester_slashings if s not in attester_slashings_in_block] + + # Return a pre state if the block is invalid + if not valid: + post_state = state return signed_block, post_state, not_included_attestations, not_included_attester_slashings @@ -592,26 +616,31 @@ def _generate_sm_link_tree(spec, genesis_state, sm_links, rnd: random.Random, de signed_blocks = signed_blocks + new_signed_blocks # Sort blocks by a slot - return sorted(signed_blocks, key=lambda b: b.message.slot), branch_tips[highest_target_sm_link] + signed_block_messages = [ProtocolMessage(b) for b in signed_blocks] + return sorted(signed_block_messages, key=lambda b: b.payload.message.slot), branch_tips[highest_target_sm_link] -def _generate_block_tree(spec, - anchor_tip: BranchTip, - rnd: random.Random, - debug, - block_parents, - with_attester_slashings) -> ([], [], []): - signed_blocks = [] +def _spoil_block(spec, rnd: random.Random, signed_block): + signed_block.message.state_root = spec.Root(rnd.randbytes(32)) + +def _generate_block_tree(spec, + anchor_tip: BranchTip, + rnd: random.Random, + debug, + block_parents, + with_attester_slashings, + with_invalid_messages) -> ([], [], []): in_block_attestations = anchor_tip.attestations.copy() post_states = [anchor_tip.beacon_state.copy()] current_slot = anchor_tip.beacon_state.slot block_index = 1 block_tree_tips = set([0]) - out_of_block_attestations = [] in_block_attester_slashings = [] - out_of_block_attester_slashings = [] attester_slashings_count = 0 + out_of_block_attestation_messages = [] + out_of_block_attester_slashing_messages = [] + signed_block_messages = [] while block_index < len(block_parents): # Propose a block if slot shouldn't be empty @@ -627,14 +656,34 @@ def _generate_block_tree(spec, # Produce block proposer = spec.get_beacon_proposer_index(parent_state) - signed_block, post_state, in_block_attestations, in_block_attester_slashings = _produce_block( - spec, parent_state, in_block_attestations, in_block_attester_slashings) - signed_blocks.append(signed_block) - post_states.append(post_state) + block_should_be_valid = rnd.randint(0, 99) >= INVALID_MESSAGES_RATE if with_invalid_messages else True - # Update tips - block_tree_tips.discard(parent_index) - block_tree_tips.add(block_index) + if block_should_be_valid: + signed_block, post_state, in_block_attestations, in_block_attester_slashings = _produce_block( + spec, parent_state, in_block_attestations, in_block_attester_slashings) + + # A block can be unintentionally invalid, e.g. a proposer was slashed + # In this case it is expected that post_state == parent_state, + # and beacon operations returned from _produce_block are expected to remain untouched + block_is_valid = post_state.latest_block_header.slot == signed_block.message.slot + + # Valid block + signed_block_messages.append(ProtocolMessage(signed_block, block_is_valid)) + post_states.append(post_state) + + # Update tips + if block_is_valid: + block_tree_tips.discard(parent_index) + block_tree_tips.add(block_index) + else: + # Intentionally invalid block + # Do not update slashings and attestations for them to be included in the future blocks + signed_block, _, _, _ = _produce_block( + spec, parent_state, in_block_attestations, in_block_attester_slashings) + _spoil_block(spec, rnd, signed_block) + signed_block_messages.append(ProtocolMessage(signed_block, False)) + # Append the parent state as the post state as if the block were not applied + post_states.append(parent_state) # Next block block_index += 1 @@ -656,18 +705,16 @@ def split_list(lst, n): attestations_in_slot = _attest_to_slot(spec, attesting_state, attesting_state.slot, lambda comm: [i for i in comm if i in attesters[index]]) - # Sample strictly out of block attestations - new_out_of_block_attestations = rnd.sample( - attestations_in_slot, len(attestations_in_slot) * OUT_OF_BLOCK_ATTESTATION_RATE // 100) - - # Prepend in block attestations to the list - new_in_block_attestations = [a for a in attestations_in_slot if a not in new_out_of_block_attestations] - in_block_attestations = new_in_block_attestations + in_block_attestations - - # Sample out of block attestations that are also included in block - new_in_and_out_of_block_attestations = rnd.sample( - new_in_block_attestations, len(new_in_block_attestations) * IN_AND_OUT_OF_BLOCK_ATTESTATION_RATE // 100) - out_of_block_attestations += new_out_of_block_attestations + new_in_and_out_of_block_attestations + # Sample on chain and off chain attestations + for a in attestations_in_slot: + choice = rnd.randint(0, 99) + if choice < OFF_CHAIN_ATTESTATION_RATE: + out_of_block_attestation_messages.append(ProtocolMessage(a, True)) + elif choice < OFF_CHAIN_ATTESTATION_RATE + ON_CHAIN_ATTESTATION_RATE: + in_block_attestations.insert(0, a) + else: + out_of_block_attestation_messages.append(ProtocolMessage(a, True)) + in_block_attestations.insert(0, a) # Create attester slashing if with_attester_slashings and attester_slashings_count < MAX_ATTESTER_SLASHINGS: @@ -680,12 +727,12 @@ def split_list(lst, n): signed_2=True) choice = rnd.randint(0, 99) - if choice < OFF_AND_ON_CHAIN_SLASHING_RATES[0]: - out_of_block_attester_slashings.append(attester_slashing) - elif choice < OFF_AND_ON_CHAIN_SLASHING_RATES[1]: + if choice < OFF_CHAIN_SLASHING_RATE: + out_of_block_attester_slashing_messages.append(ProtocolMessage(attester_slashing, True)) + elif choice < OFF_CHAIN_SLASHING_RATE + ON_CHAIN_SLASHING_RATE: in_block_attester_slashings.append(attester_slashing) else: - out_of_block_attester_slashings.append(attester_slashing) + out_of_block_attester_slashing_messages.append(ProtocolMessage(attester_slashing, True)) in_block_attester_slashings.append(attester_slashing) attester_slashings_count += 1 @@ -695,18 +742,20 @@ def split_list(lst, n): if debug: print('\nblock_tree:') - print('blocks: ', _print_block_tree(spec, post_states[0], signed_blocks)) + print('blocks: ', _print_block_tree(spec, post_states[0], [b.payload for b in signed_block_messages])) print(' ', 'state.current_justified_checkpoint:', '(epoch=' + str(post_states[len(post_states) - 1].current_justified_checkpoint.epoch) + ', root=' + str(post_states[len(post_states) - 1].current_justified_checkpoint.root)[:6] + ')') print('on_attestation: ') - print(' ', 'count =', len(out_of_block_attestations)) + print(' ', 'count =', len(out_of_block_attestation_messages)) + print(' ', 'valid =', len([a for a in out_of_block_attestation_messages if a.valid])) print('on_attester_slashing: ') - print(' ', 'count =', len(out_of_block_attester_slashings)) + print(' ', 'count =', len(out_of_block_attester_slashing_messages)) + print(' ', 'valid =', len([s for s in out_of_block_attester_slashing_messages if s.valid])) - return (sorted(signed_blocks, key=lambda b: b.message.slot), - sorted(out_of_block_attestations, key=lambda a: a.data.slot), - sorted(out_of_block_attester_slashings, key=lambda a: a.attestation_1.data.slot)) + return (sorted(signed_block_messages, key=lambda b: b.payload.message.slot), + sorted(out_of_block_attestation_messages, key=lambda a: a.payload.data.slot), + sorted(out_of_block_attester_slashing_messages, key=lambda a: a.payload.attestation_1.data.slot)) def _print_head(spec, store): @@ -724,9 +773,16 @@ def _on_tick_and_append_step(spec, store, slot, test_steps): on_tick_and_append_step(spec, store, time, test_steps) assert store.time == time + @with_altair_and_later @spec_state_test -def test_sm_links_tree_model(spec, state, debug=False, seed=1, sm_links=None, with_attester_slashings=False): +def test_sm_links_tree_model(spec, + state, + debug=False, + seed=1, + sm_links=None, + with_attester_slashings=False, + with_invalid_messages=False): block_parents = [0, 0, 0, 2, 2, 1, 1, 6, 6, 5, 5, 4, 7, 4, 3, 3] # This test is mainly used for the test generation purposes @@ -741,7 +797,7 @@ def test_sm_links_tree_model(spec, state, debug=False, seed=1, sm_links=None, wi # Find a reachable solution trying with different seeds if needed # sm_links constraints may not have a solution beacause of the randomization affecting validator partitions - signed_blocks = [] + signed_block_messages = [] highest_tip = BranchTip(state, [], [], state.current_justified_checkpoint) while True: if debug: @@ -749,8 +805,8 @@ def test_sm_links_tree_model(spec, state, debug=False, seed=1, sm_links=None, wi print('\nsm_links:', sm_links) rnd = random.Random(seed) - signed_blocks, highest_tip = _generate_sm_link_tree(spec, state, sm_links, rnd, debug) - if len(signed_blocks) > 0: + signed_block_messages, highest_tip = _generate_sm_link_tree(spec, state, sm_links, rnd, debug) + if len(signed_block_messages) > 0: break new_seed = rnd.randint(1, 10000) @@ -759,15 +815,15 @@ def test_sm_links_tree_model(spec, state, debug=False, seed=1, sm_links=None, wi seed = new_seed # Block tree model - attestations = [] - attester_slashings = [] + attestation_messages = [] + attester_slashing_messages = [] if block_parents is not None: - block_tree, attestations, attester_slashings = _generate_block_tree( - spec, highest_tip, rnd, debug, block_parents, with_attester_slashings) + block_tree, attestation_messages, attester_slashing_messages = _generate_block_tree( + spec, highest_tip, rnd, debug, block_parents, with_attester_slashings, with_invalid_messages) # Merge block_tree and sm_link_tree blocks - block_tree_root_slot = block_tree[0].message.slot - signed_blocks = [sb for sb in signed_blocks if sb.message.slot < block_tree_root_slot] - signed_blocks = signed_blocks + block_tree + block_tree_root_slot = block_tree[0].payload.message.slot + signed_block_messages = [b for b in signed_block_messages if b.payload.message.slot < block_tree_root_slot] + signed_block_messages = signed_block_messages + block_tree # Yield run parameters yield 'seed', 'meta', seed @@ -781,11 +837,12 @@ def test_sm_links_tree_model(spec, state, debug=False, seed=1, sm_links=None, wi _on_tick_and_append_step(spec, store, state.slot, test_steps) # Apply generated messages - max_block_slot = max(b.message.slot for b in signed_blocks) - max_attestation_slot = max(a.data.slot for a in attestations) if any(attestations) else 0 - max_slashing_slot = max(s.attestation_1.data.slot for s in attester_slashings) if any(attester_slashings) else 0 + max_block_slot = max(b.payload.message.slot for b in signed_block_messages) + max_attestation_slot = max(a.payload.data.slot for a in attestation_messages) if any(attestation_messages) else 0 + max_slashing_slot = max( + s.payload.attestation_1.data.slot for s in attester_slashing_messages) if any(attester_slashing_messages) else 0 - start_slot = min(b.message.slot for b in signed_blocks) + start_slot = min(b.payload.message.slot for b in signed_block_messages) end_slot = max(max_block_slot, max_attestation_slot, max_slashing_slot) + 1 # Advance time to start_slot @@ -797,18 +854,23 @@ def test_sm_links_tree_model(spec, state, debug=False, seed=1, sm_links=None, wi _on_tick_and_append_step(spec, store, slot, test_steps) # on_attestation for attestations from the previous slot - for attestation in (a for a in attestations if a.data.slot == slot - 1): - yield from add_attestation(spec, store, attestation, test_steps) + for attestation_message in (a for a in attestation_messages if a.payload.data.slot == slot - 1): + yield from add_attestation(spec, store, attestation_message.payload, test_steps, attestation_message.valid) # on_attester_slashing for slashing from the previous slot - for attester_slashing in (s for s in attester_slashings if s.attestation_1.data.slot == slot - 1): - yield from add_attester_slashing(spec, store, attester_slashing, test_steps) + for attester_slashing_message in (s for s in attester_slashing_messages + if s.payload.attestation_1.data.slot == slot - 1): + yield from add_attester_slashing(spec, store, attester_slashing_message.payload, + test_steps, attester_slashing_message.valid) # on_block for blocks from the current slot - for block in (b for b in signed_blocks if b.message.slot == slot): - yield from tick_and_add_block(spec, store, block, test_steps) - block_root = block.message.hash_tree_root() - assert store.blocks[block_root] == block.message + for signed_block_message in (b for b in signed_block_messages if b.payload.message.slot == slot): + yield from add_block(spec, store, signed_block_message.payload, test_steps, signed_block_message.valid) + block_root = signed_block_message.payload.message.hash_tree_root() + if signed_block_message.valid: + assert store.blocks[block_root] == signed_block_message.payload.message + else: + assert block_root not in store.blocks.values() if debug: print(' head: ' + _print_head(spec, store)) diff --git a/tests/generators/fork_choice_generated/main.py b/tests/generators/fork_choice_generated/main.py index 00f8f8a4cc..ecb5689fc8 100644 --- a/tests/generators/fork_choice_generated/main.py +++ b/tests/generators/fork_choice_generated/main.py @@ -63,7 +63,8 @@ def _create_providers(forks: Iterable[SpecForkName], initial_seed: int, solutions: Iterable[Iterable[tuple]], number_of_variations: int, - with_attester_slashings: bool) -> Iterable[TestProvider]: + with_attester_slashings: bool, + with_invalid_messages: bool) -> Iterable[TestProvider]: def prepare_fn() -> None: bls.use_milagro() return @@ -95,7 +96,8 @@ def make_cases_fn() -> Iterable[TestCase]: debug=debug, seed=seed, sm_links=solution, - with_attester_slashings=with_attester_slashings)) + with_attester_slashings=with_attester_slashings, + with_invalid_messages=with_invalid_messages)) yield TestProvider(prepare=prepare_fn, make_cases=make_cases_fn) @@ -174,5 +176,6 @@ def make_cases_fn() -> Iterable[TestCase]: initial_seed=args.fc_gen_seed, solutions=solutions, number_of_variations=args.fc_gen_variations, - with_attester_slashings=True), + with_attester_slashings=True, + with_invalid_messages=True), arg_parser) diff --git a/tests/generators/fork_choice_generated/test_gen.py b/tests/generators/fork_choice_generated/test_gen.py index 6815025bf9..d18cca7be6 100644 --- a/tests/generators/fork_choice_generated/test_gen.py +++ b/tests/generators/fork_choice_generated/test_gen.py @@ -69,7 +69,9 @@ debug=args.fc_gen_debug, initial_seed=initial_seed, solutions=solutions, - number_of_variations=nr_variations) + number_of_variations=nr_variations, + with_attester_slashings=False, + with_invalid_messages=False) elif test_type == 'block_cover': solutions = block_cover_load_solutions(instances_path) providers = block_cover_create_providers(forks=block_cover_forks, From b56338157fa4a77bb6f2886cb29465ac7673865c Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Tue, 9 Apr 2024 15:47:07 +0600 Subject: [PATCH 030/111] Omit valid: true for on_attestation --- tests/core/pyspec/eth2spec/test/helpers/fork_choice.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py index 48e5b579fa..72f4569c2a 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py +++ b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py @@ -83,10 +83,10 @@ def run_func(): def add_attestation(spec, store, attestation, test_steps, is_from_block=False, valid=True): run_on_attestation(spec, store, attestation, is_from_block=is_from_block, valid=valid) yield get_attestation_file_name(attestation), attestation - test_steps.append({ - 'attestation': get_attestation_file_name(attestation), - 'valid': valid, - }) + if valid: + test_steps.append({'attestation': get_attestation_file_name(attestation)}) + else: + test_steps.append({'attestation': get_attestation_file_name(attestation), 'valid': False}) def add_attestations(spec, store, attestations, test_steps, is_from_block=False): From e59e612b814283adc95f27e155a47225f15e5f0c Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Wed, 10 Apr 2024 00:24:41 +0400 Subject: [PATCH 031/111] Mutation operators/shuffling added. --- .../filter_block_tree_generator.py | 52 ++-- .../generators/fork_choice_generated/main.py | 53 ++-- .../mutation_operators.py | 248 ++++++++++++++++++ 3 files changed, 320 insertions(+), 33 deletions(-) create mode 100644 tests/generators/fork_choice_generated/mutation_operators.py diff --git a/tests/generators/fork_choice_generated/filter_block_tree_generator.py b/tests/generators/fork_choice_generated/filter_block_tree_generator.py index a1ffab1815..e5a0b6681b 100644 --- a/tests/generators/fork_choice_generated/filter_block_tree_generator.py +++ b/tests/generators/fork_choice_generated/filter_block_tree_generator.py @@ -1,12 +1,14 @@ from eth2spec.test.helpers.constants import ALTAIR from eth2spec.gen_helpers.gen_base import gen_runner from eth2spec.test.helpers.constants import MINIMAL +from eth2spec.test.helpers.specs import spec_targets from eth2spec.gen_helpers.gen_base.gen_typing import TestCase, TestProvider from typing import Iterable from importlib import import_module from eth2spec.utils import bls from eth2spec.test.helpers.typing import SpecForkName, PresetBaseName from minizinc import Instance, Model, Solver +from mutation_operators import MutatorsGenerator import random from ruamel.yaml import YAML @@ -102,13 +104,22 @@ def _create_providers(forks: Iterable[SpecForkName], debug: bool, initial_seed: int, solutions, - number_of_variations: int) -> Iterable[TestProvider]: + number_of_variations: int, + number_of_mutations: int) -> Iterable[TestProvider]: def prepare_fn() -> None: bls.use_milagro() return def make_cases_fn() -> Iterable[TestCase]: - test_fn = _import_test_fn() + _test_fn = _import_test_fn() + def test_fn(phase: str, preset: str, seed: int, solution): + return _test_fn(generator_mode=True, + phase=phase, + preset=preset, + bls_active=BLS_ACTIVE, + debug=debug, + seed=seed, + model_params=solution) seeds = [initial_seed] if number_of_variations > 1: @@ -120,19 +131,19 @@ def make_cases_fn() -> Iterable[TestCase]: for seed in seeds: for fork_name in forks: for preset_name in presets: - yield TestCase(fork_name=fork_name, - preset_name=preset_name, - runner_name=GENERATOR_NAME, - handler_name='filter_block_tree_model', - suite_name='fork_choice', - case_name='filter_block_tree_model_' + str(i) + '_' + str(seed), - case_fn=lambda: test_fn(generator_mode=True, - phase=fork_name, - preset=preset_name, - bls_active=BLS_ACTIVE, - debug=debug, - seed=seed, - model_params=solution)) + spec = spec_targets[preset_name][fork_name] + mutation_generator = MutatorsGenerator( + spec, seed, number_of_mutations, + lambda: test_fn(fork_name, preset_name, seed, solution), + debug=debug) + for j in range(1 + number_of_mutations): + yield TestCase(fork_name=fork_name, + preset_name=preset_name, + runner_name=GENERATOR_NAME, + handler_name='filter_block_tree_model', + suite_name='fork_choice', + case_name='filter_block_tree_model_' + str(i) + '_' + str(seed) + '_' + str(j), + case_fn=mutation_generator.next_test_case) yield TestProvider(prepare=prepare_fn, make_cases=make_cases_fn) @@ -180,6 +191,14 @@ def make_cases_fn() -> Iterable[TestCase]: required=False, help='Number of solutions per MiniZinc query' ) + arg_parser.add_argument( + '--fc-gen-mutations', + dest='fc_gen_mutations', + default=0, + type=int, + required=False, + help='Number of mutations per base test case' + ) arg_parser.add_argument( '--fc-gen-instances-path', dest='fc_gen_instances_path', @@ -202,5 +221,6 @@ def make_cases_fn() -> Iterable[TestCase]: debug=args.fc_gen_debug, initial_seed=args.fc_gen_seed, solutions=solutions, - number_of_variations=args.fc_gen_variations), + number_of_variations=args.fc_gen_variations, + number_of_mutations=args.fc_gen_mutations), arg_parser) diff --git a/tests/generators/fork_choice_generated/main.py b/tests/generators/fork_choice_generated/main.py index ecb5689fc8..872f8860c3 100644 --- a/tests/generators/fork_choice_generated/main.py +++ b/tests/generators/fork_choice_generated/main.py @@ -1,6 +1,7 @@ from eth2spec.test.helpers.constants import ALTAIR from eth2spec.gen_helpers.gen_base import gen_runner from eth2spec.test.helpers.constants import MINIMAL +from eth2spec.test.helpers.specs import spec_targets from eth2spec.gen_helpers.gen_base.gen_typing import TestCase, TestProvider from typing import Iterable from importlib import import_module @@ -8,6 +9,7 @@ from eth2spec.test.helpers.typing import SpecForkName, PresetBaseName from minizinc import Instance, Model, Solver from ruamel.yaml import YAML +from mutation_operators import mk_mutations, MutatorsGenerator import random @@ -63,6 +65,7 @@ def _create_providers(forks: Iterable[SpecForkName], initial_seed: int, solutions: Iterable[Iterable[tuple]], number_of_variations: int, + number_of_mutations: int, with_attester_slashings: bool, with_invalid_messages: bool) -> Iterable[TestProvider]: def prepare_fn() -> None: @@ -70,8 +73,17 @@ def prepare_fn() -> None: return def make_cases_fn() -> Iterable[TestCase]: - test_fn = _import_test_fn() - # solutions = _find_sm_link_solutions(anchor_epoch, number_of_epochs, number_of_links) + _test_fn = _import_test_fn() + def test_fn(phase: str, preset: str, seed: int, solution): + return _test_fn(generator_mode=True, + phase=phase, + preset=preset, + bls_active=BLS_ACTIVE, + debug=debug, + seed=seed, + sm_links=solution, + with_attester_slashings=with_attester_slashings, + with_invalid_messages=with_invalid_messages) seeds = [initial_seed] if number_of_variations > 1: @@ -83,21 +95,19 @@ def make_cases_fn() -> Iterable[TestCase]: for seed in seeds: for fork_name in forks: for preset_name in presets: - yield TestCase(fork_name=fork_name, - preset_name=preset_name, - runner_name=GENERATOR_NAME, - handler_name='sm_links_tree_model', - suite_name='fork_choice', - case_name='sm_links_tree_model_' + str(i) + '_' + str(seed), - case_fn=lambda: test_fn(generator_mode=True, - phase=fork_name, - preset=preset_name, - bls_active=BLS_ACTIVE, - debug=debug, - seed=seed, - sm_links=solution, - with_attester_slashings=with_attester_slashings, - with_invalid_messages=with_invalid_messages)) + spec = spec_targets[preset_name][fork_name] + mutation_generator = MutatorsGenerator( + spec, seed, number_of_mutations, + lambda: test_fn(fork_name, preset_name, seed, solution), + debug=debug) + for j in range(1 + number_of_mutations): + yield TestCase(fork_name=fork_name, + preset_name=preset_name, + runner_name=GENERATOR_NAME, + handler_name='sm_links_tree_model', + suite_name='fork_choice', + case_name='sm_links_tree_model_' + str(i) + '_' + str(seed) + '_' + str(j), + case_fn=mutation_generator.next_test_case) yield TestProvider(prepare=prepare_fn, make_cases=make_cases_fn) @@ -153,6 +163,14 @@ def make_cases_fn() -> Iterable[TestCase]: required=False, help='Number of super majority links per solution' ) + arg_parser.add_argument( + '--fc-gen-mutations', + dest='fc_gen_mutations', + default=0, + type=int, + required=False, + help='Number of mutations per base test case' + ) arg_parser.add_argument( '--fc-gen-instances-path', dest='fc_gen_instances_path', @@ -176,6 +194,7 @@ def make_cases_fn() -> Iterable[TestCase]: initial_seed=args.fc_gen_seed, solutions=solutions, number_of_variations=args.fc_gen_variations, + number_of_mutations=args.fc_gen_mutations, with_attester_slashings=True, with_invalid_messages=True), arg_parser) diff --git a/tests/generators/fork_choice_generated/mutation_operators.py b/tests/generators/fork_choice_generated/mutation_operators.py new file mode 100644 index 0000000000..e5ae9facf3 --- /dev/null +++ b/tests/generators/fork_choice_generated/mutation_operators.py @@ -0,0 +1,248 @@ +from dataclasses import dataclass +from eth2spec.test.helpers.fork_choice import ( + on_tick_and_append_step, output_store_checks +) +from eth2spec.utils import bls +import random + + +@dataclass +class FCTestCase: + meta: dict + anchor_block: object + anchor_state: object + blocks: dict + atts: dict + slashings: dict + steps: list + + def with_steps(self, steps): + return FCTestCase(self.meta, self.anchor_block, self.anchor_state, self.blocks, self.atts, self.slashings, steps) + + def dump(self): + for k,v in self.meta.items(): + yield k, 'meta', v + yield 'anchor_state', 'ssz', self.anchor_state + yield 'anchor_block', 'ssz', self.anchor_block + for k,v in self.blocks.items(): + yield k, 'ssz', v + for k,v in self.atts.items(): + yield k, 'ssz', v + for k,v in self.slashings.items(): + yield k, 'ssz', v + yield 'steps', 'data', self.steps + + +def parse_test_case(test_case): + meta = {} + anchor_block = None + anchor_state = None + blocks = {} + atts = {} + slashings = {} + steps = None + for i, elem in enumerate(test_case): + assert isinstance(elem, tuple) and len(elem) == 3 + if elem[1] == 'meta': + meta[elem[0]] = elem[2] + elif elem[1] == 'ssz': + if elem[0] == 'anchor_state': + assert anchor_state is None + anchor_state = elem[2] + elif elem[0] == 'anchor_block': + assert anchor_block is None + anchor_block = elem[2] + elif elem[0].startswith('block_'): + blocks[elem[0]] = elem[2] + elif elem[0].startswith('attestation_'): + atts[elem[0]] = elem[2] + elif elem[0].startswith('attester_slashing_'): + slashings[elem[0]] = elem[2] + else: + raise ValueError(f'not implemented {elem[0]}/{elem[1]}') + elif elem[1] == 'data' and elem[0] == 'steps': + assert steps is None + steps = elem[2] + else: + raise ValueError(f'not implemented {elem[0]}/{elem[1]}') + return FCTestCase(meta, anchor_block, anchor_state, blocks, atts, slashings, steps) + + +def steps_to_events(steps): + curr = 0 + events = [] + for step in steps: + if 'tick' in step: + curr = step['tick'] + elif 'block' in step: + events.append((curr, ('block', step['block']))) + elif 'attestation' in step: + events.append((curr, ('attestation', step['attestation']))) + elif 'attester_slashing' in step: + events.append((curr, ('attester_slashing', step['attester_slashing']))) + elif 'checks' in step or 'property_checks' in step: + pass + else: + assert False, step + return events + + +def events_to_steps(events): + steps = [] + for (time, event) in events: + steps.append({'tick': int(time)}) + steps.append({event[0]: event[1]}) + return steps + + +def mut_shift_(tv, idx, delta): + time, event = tv[idx] + new_time = int(time) + delta + if new_time >= 0: + return sorted(tv[:idx] + [(new_time, event)] + tv[idx+1:], key=lambda x: x[0]) + + +def mut_shift(tv, rnd: random.Random): + idx = rnd.choice(range(len(tv))) + idx_time = tv[idx][0] + dir = rnd.randint(0, 1) + if idx_time == 0 or dir: + time_shift = rnd.randint(0, 6) * 3 + else: + time_shift = -rnd.randint(0, idx_time // 3) + return mut_shift_(tv, idx, time_shift) + + +def mut_drop_(tv, idx): + return tv[:idx] + tv[idx+1:] + + +def mut_drop(tv, rnd: random.Random): + idx = rnd.choice(range(len(tv))) + return mut_drop_(tv, idx) + + +def mut_dup_(tv, idx, shift): + return mut_shift_(tv + [tv[idx]], len(tv), shift) + + +def mutate_tc(rnd, initial_tv, cnt, debug=False): + tv_ = initial_tv + for i in range(cnt): + coin = rnd.randint(0, 1) + if coin: + if debug: + print(" mutating initial tv") + tv__ = initial_tv + else: + if debug: + print(" mutating tv_") + tv__ = tv_ + tv = tv__ + op_kind = rnd.randint(0, 2) + if op_kind == 0: + idx = rnd.choice(range(len(tv))) + if debug: + print(f" dropping {idx}") + tv_ = mut_drop_(tv, idx) + elif op_kind == 1: + idx = rnd.choice(range(len(tv))) + idx_time = tv[idx][0] + dir = rnd.randint(0, 1) + if idx_time == 0 or dir: + time_shift = rnd.randint(0, 6) * 3 + else: + time_shift = -rnd.randint(0, idx_time // 3) * 3 + if debug: + print(f" shifting {idx} by {time_shift}") + tv_ = mut_shift_(tv, idx, time_shift) + elif op_kind == 2: + idx = rnd.choice(range(len(tv))) + shift = rnd.randint(0, 5) * 3 + if debug: + print(f" dupping {idx} and shifting by {shift}") + tv_ = mut_dup_(tv, idx, shift) + else: + assert False + yield tv_ + + +def update_test_case(spec, fc_test_case: FCTestCase, events): + old_bls_state = bls.bls_active + bls.bls_active = False + try: + anchor_state = spec.BeaconState.decode_bytes(fc_test_case.anchor_state) + anchor_block = spec.BeaconBlock.decode_bytes(fc_test_case.anchor_block) + store = spec.get_forkchoice_store(anchor_state, anchor_block) + test_steps = [] + for (time, (kind, event)) in events: + on_tick_and_append_step(spec, store, time, test_steps) + + if kind == 'block': + block_id = event + sb = spec.SignedBeaconBlock.decode_bytes(fc_test_case.blocks[block_id]) + try: + spec.on_block(store, sb) + for attestation in sb.message.body.attestations: + spec.on_attestation(store, attestation, is_from_block=True) + + for attester_slashing in sb.message.body.attester_slashings: + spec.on_attester_slashing(store, attester_slashing) + + valid = True + except AssertionError as e: + valid = False + test_steps.append({'block': block_id, 'valid': valid}) + output_store_checks(spec, store, test_steps) + elif kind == 'attestation': + att_id = event + att = spec.Attestation.decode_bytes(fc_test_case.atts[att_id]) + try: + spec.on_attestation(store, att, is_from_block=False) + valid = True + except AssertionError as e: + valid = False + test_steps.append({'attestation': att_id, 'valid': valid}) + output_store_checks(spec, store, test_steps) + elif kind == 'attester_slashing': + slashing_id = event + slashing = spec.AttesterSlashing.decode_bytes(fc_test_case.slashings[slashing_id]) + try: + spec.on_attester_slashing(store, slashing) + valid = True + except AssertionError as e: + valid = False + test_steps.append({'attester_slashing': slashing_id, 'valid': valid}) + output_store_checks(spec, store, test_steps) + else: + raise ValueError(f'not implemented {kind}') + next_slot_time = store.genesis_time + (spec.get_current_slot(store) + 1) * spec.config.SECONDS_PER_SLOT + on_tick_and_append_step(spec, store, next_slot_time, test_steps) + + return fc_test_case.with_steps(test_steps) + finally: + bls.bls_active = old_bls_state + + +def mk_mutations(spec, seed, num, test_fn, debug=False): + if debug: + print('make base case') + base = list(test_fn()) + yield 0, base + rnd = random.Random(seed) + + fc_test_case = parse_test_case(base) + events = steps_to_events(fc_test_case.steps) + for i, tv_ in enumerate(mutate_tc(rnd, events, num, debug=debug)): + if debug: + print('make mutant', i+1) + yield i+1, update_test_case(spec, fc_test_case, tv_).dump() + + +class MutatorsGenerator: + def __init__(self, spec, seed, num, test_fn, debug=False): + self.iterator = iter(mk_mutations(spec, seed, num, test_fn, debug)) + + def next_test_case(self): + _, test_case = next(self.iterator) + return test_case From cedc82fcb745223ee1ffbb637823640120c89b9d Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Wed, 10 Apr 2024 04:14:00 +0400 Subject: [PATCH 032/111] Added customizeable test_name. test_gen supports `nr_mutations`, `with_attester_slashings` and `with_invalid_messages` parameters. --- .../filter_block_tree_generator.py | 10 +++-- .../generators/fork_choice_generated/main.py | 10 +++-- .../fork_choice_generated/test_gen.py | 39 +++++++++++++++---- 3 files changed, 44 insertions(+), 15 deletions(-) diff --git a/tests/generators/fork_choice_generated/filter_block_tree_generator.py b/tests/generators/fork_choice_generated/filter_block_tree_generator.py index e5a0b6681b..db0cf26dd0 100644 --- a/tests/generators/fork_choice_generated/filter_block_tree_generator.py +++ b/tests/generators/fork_choice_generated/filter_block_tree_generator.py @@ -99,7 +99,8 @@ def _generate_model_solutions(anchor_epoch: int, nr_solutions: int=5): return solutions -def _create_providers(forks: Iterable[SpecForkName], +def _create_providers(test_name: str, /, + forks: Iterable[SpecForkName], presets: Iterable[PresetBaseName], debug: bool, initial_seed: int, @@ -140,9 +141,9 @@ def test_fn(phase: str, preset: str, seed: int, solution): yield TestCase(fork_name=fork_name, preset_name=preset_name, runner_name=GENERATOR_NAME, - handler_name='filter_block_tree_model', + handler_name=test_name, suite_name='fork_choice', - case_name='filter_block_tree_model_' + str(i) + '_' + str(seed) + '_' + str(j), + case_name=test_name + '_' + str(i) + '_' + str(seed) + '_' + str(j), case_fn=mutation_generator.next_test_case) yield TestProvider(prepare=prepare_fn, make_cases=make_cases_fn) @@ -216,7 +217,8 @@ def test_fn(phase: str, preset: str, seed: int, solution): solutions = _generate_model_solutions(args.fc_gen_anchor_epoch, args.fc_gen_nr_solutions) gen_runner.run_generator(GENERATOR_NAME, - _create_providers(forks=forks, + _create_providers('filter_block_tree_model', + forks=forks, presets=presets, debug=args.fc_gen_debug, initial_seed=args.fc_gen_seed, diff --git a/tests/generators/fork_choice_generated/main.py b/tests/generators/fork_choice_generated/main.py index 872f8860c3..fc842a74d6 100644 --- a/tests/generators/fork_choice_generated/main.py +++ b/tests/generators/fork_choice_generated/main.py @@ -59,7 +59,8 @@ def _load_sm_link_solutions(instance_path: str) -> Iterable[Iterable[tuple]]: yield list(zip(solution['sources'], solution['targets'])) -def _create_providers(forks: Iterable[SpecForkName], +def _create_providers(test_name: str, /, + forks: Iterable[SpecForkName], presets: Iterable[PresetBaseName], debug: bool, initial_seed: int, @@ -104,9 +105,9 @@ def test_fn(phase: str, preset: str, seed: int, solution): yield TestCase(fork_name=fork_name, preset_name=preset_name, runner_name=GENERATOR_NAME, - handler_name='sm_links_tree_model', + handler_name=test_name, suite_name='fork_choice', - case_name='sm_links_tree_model_' + str(i) + '_' + str(seed) + '_' + str(j), + case_name=test_name + '_' + str(i) + '_' + str(seed) + '_' + str(j), case_fn=mutation_generator.next_test_case) yield TestProvider(prepare=prepare_fn, make_cases=make_cases_fn) @@ -188,7 +189,8 @@ def test_fn(phase: str, preset: str, seed: int, solution): solutions = _find_sm_link_solutions(args.fc_gen_anchor_epoch, args.fc_gen_epochs, args.fc_gen_links) gen_runner.run_generator(GENERATOR_NAME, - _create_providers(forks=forks, + _create_providers('sm_links_tree_model', + forks=forks, presets=presets, debug=args.fc_gen_debug, initial_seed=args.fc_gen_seed, diff --git a/tests/generators/fork_choice_generated/test_gen.py b/tests/generators/fork_choice_generated/test_gen.py index d18cca7be6..c3ce34ab4d 100644 --- a/tests/generators/fork_choice_generated/test_gen.py +++ b/tests/generators/fork_choice_generated/test_gen.py @@ -22,13 +22,31 @@ 'test_type': 'block_tree', 'instances': 'block_tree.yaml', 'seed': 123, - 'nr_variations': 3, + 'nr_variations': 1, + 'nr_mutations': 0, + }, + 'invalid_message_test': { + 'test_type': 'block_tree', + 'with_invalid_messages': True, + 'instances': 'block_tree.yaml', + 'seed': 123, + 'nr_variations': 1, + 'nr_mutations': 1, + }, + 'attester_slashing_test': { + 'test_type': 'block_tree', + 'with_attester_slashings': True, + 'instances': 'block_tree.yaml', + 'seed': 123, + 'nr_variations': 1, + 'nr_mutations': 1, }, 'block_cover_test': { 'test_type': 'block_cover', 'instances': 'block_cover.yaml', 'seed': 456, - 'nr_variations': 3, + 'nr_variations': 1, + 'nr_mutations': 0, } } @@ -61,25 +79,32 @@ instances_path = params['instances'] initial_seed = params['seed'] nr_variations = params['nr_variations'] + nr_mutations = params['nr_mutations'] + with_attester_slashings = params.get('with_attester_slashings', False) + with_invalid_messages = params.get('with_invalid_messages', False) if test_type == 'block_tree': solutions = block_tree_load_solutions(instances_path) - providers = block_tree_create_providers(forks=block_tree_forks, + providers = block_tree_create_providers(test_name, + forks=block_tree_forks, presets=block_tree_presets, debug=args.fc_gen_debug, initial_seed=initial_seed, solutions=solutions, number_of_variations=nr_variations, - with_attester_slashings=False, - with_invalid_messages=False) + number_of_mutations=nr_mutations, + with_attester_slashings=with_attester_slashings, + with_invalid_messages=with_invalid_messages) elif test_type == 'block_cover': solutions = block_cover_load_solutions(instances_path) - providers = block_cover_create_providers(forks=block_cover_forks, + providers = block_cover_create_providers(test_name, + forks=block_cover_forks, presets=block_cover_presets, debug=args.fc_gen_debug, initial_seed=initial_seed, solutions=solutions, - number_of_variations=nr_variations) + number_of_variations=nr_variations, + number_of_mutations=nr_mutations) else: raise ValueError(f'Unsupported test type: {test_type}') From b3dae8f9cdcec9fee1133897f9a37ee34af246ec Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 10 Apr 2024 20:45:45 +0600 Subject: [PATCH 033/111] Pass block parents as a parameter, update block tree model --- .../fork_choice/test_sm_links_tree_model.py | 13 +++---- .../generators/fork_choice_generated/main.py | 36 ++++++++++++++----- .../model/minizinc/Block_tree.mzn | 20 +++++------ .../fork_choice_generated/test_gen.py | 6 ++-- 4 files changed, 49 insertions(+), 26 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py index 581507d665..5b664822d8 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py @@ -781,14 +781,13 @@ def test_sm_links_tree_model(spec, debug=False, seed=1, sm_links=None, + block_parents=None, with_attester_slashings=False, with_invalid_messages=False): - block_parents = [0, 0, 0, 2, 2, 1, 1, 6, 6, 5, 5, 4, 7, 4, 3, 3] - # This test is mainly used for the test generation purposes - # Thus seed and sm_links are provided by the generator - # Define sm_links and seed explicitly to execute a certain run of this test - if sm_links is None: + # Thus seed, sm_links and block_parents are provided by the generator + # Define sm_links, seed and block_parents explicitly to execute a certain run of this test + if sm_links is None or block_parents is None: return assert (1, 2) not in sm_links, '(1, 2) sm link is not supported due to unsatisfiability' @@ -802,7 +801,8 @@ def test_sm_links_tree_model(spec, while True: if debug: print('\nseed:', seed) - print('\nsm_links:', sm_links) + print('sm_links:', sm_links) + print('block_parents:', block_parents) rnd = random.Random(seed) signed_block_messages, highest_tip = _generate_sm_link_tree(spec, state, sm_links, rnd, debug) @@ -828,6 +828,7 @@ def test_sm_links_tree_model(spec, # Yield run parameters yield 'seed', 'meta', seed yield 'sm_links', 'meta', str(sm_links) + yield 'block_parents', 'meta', str(block_parents) test_steps = [] # Store initialization diff --git a/tests/generators/fork_choice_generated/main.py b/tests/generators/fork_choice_generated/main.py index fc842a74d6..5d86af27d0 100644 --- a/tests/generators/fork_choice_generated/main.py +++ b/tests/generators/fork_choice_generated/main.py @@ -52,6 +52,19 @@ def _find_sm_link_solutions(anchor_epoch: int, yield [_ for _ in zip(solutions[i, 'sources'], solutions[i, 'targets'])] +def _find_block_tree_solutions(number_of_blocks: int, + max_children: int, + number_of_solutions: int) -> Iterable[Iterable[int]]: + model = Model('./model/minizinc/Block_tree.mzn') + solver = Solver.lookup("gecode") + instance = Instance(solver, model) + instance['NB'] = number_of_blocks + instance['MC'] = max_children + + solutions = instance.solve(nr_solutions=number_of_solutions) + return [s.parent for s in solutions] + + def _load_sm_link_solutions(instance_path: str) -> Iterable[Iterable[tuple]]: solutions = yaml.load(open(instance_path, 'r')) print('solutions', solutions) @@ -64,7 +77,8 @@ def _create_providers(test_name: str, /, presets: Iterable[PresetBaseName], debug: bool, initial_seed: int, - solutions: Iterable[Iterable[tuple]], + sm_link_solutions: Iterable[Iterable[tuple]], + block_tree_solutions: [[int]], number_of_variations: int, number_of_mutations: int, with_attester_slashings: bool, @@ -75,14 +89,16 @@ def prepare_fn() -> None: def make_cases_fn() -> Iterable[TestCase]: _test_fn = _import_test_fn() - def test_fn(phase: str, preset: str, seed: int, solution): + + def test_fn(phase: str, preset: str, seed: int, sm_links, block_parents): return _test_fn(generator_mode=True, phase=phase, preset=preset, bls_active=BLS_ACTIVE, debug=debug, seed=seed, - sm_links=solution, + sm_links=sm_links, + block_parents=block_parents, with_attester_slashings=with_attester_slashings, with_invalid_messages=with_invalid_messages) @@ -92,14 +108,15 @@ def test_fn(phase: str, preset: str, seed: int, solution): seeds = [rnd.randint(1, 10000) for _ in range(number_of_variations)] seeds[0] = initial_seed - for i, solution in enumerate(solutions): + for i, sm_links in enumerate(sm_link_solutions): + block_parents = block_tree_solutions[i % (len(block_tree_solutions) - 1)] for seed in seeds: for fork_name in forks: for preset_name in presets: spec = spec_targets[preset_name][fork_name] mutation_generator = MutatorsGenerator( spec, seed, number_of_mutations, - lambda: test_fn(fork_name, preset_name, seed, solution), + lambda: test_fn(fork_name, preset_name, seed, sm_links, block_parents), debug=debug) for j in range(1 + number_of_mutations): yield TestCase(fork_name=fork_name, @@ -184,9 +201,11 @@ def test_fn(phase: str, preset: str, seed: int, solution): args = arg_parser.parse_args() if args.fc_gen_instances_path is not None: - solutions = _load_sm_link_solutions(args.fc_gen_instances_path) + sm_link_solutions = _load_sm_link_solutions(args.fc_gen_instances_path) + block_tree_solutions = _find_block_tree_solutions(16, 3, 3) else: - solutions = _find_sm_link_solutions(args.fc_gen_anchor_epoch, args.fc_gen_epochs, args.fc_gen_links) + sm_link_solutions = _find_sm_link_solutions(args.fc_gen_anchor_epoch, args.fc_gen_epochs, args.fc_gen_links) + block_tree_solutions = _find_block_tree_solutions(16, 3, 3) gen_runner.run_generator(GENERATOR_NAME, _create_providers('sm_links_tree_model', @@ -194,7 +213,8 @@ def test_fn(phase: str, preset: str, seed: int, solution): presets=presets, debug=args.fc_gen_debug, initial_seed=args.fc_gen_seed, - solutions=solutions, + sm_link_solutions=sm_link_solutions, + block_tree_solutions=block_tree_solutions, number_of_variations=args.fc_gen_variations, number_of_mutations=args.fc_gen_mutations, with_attester_slashings=True, diff --git a/tests/generators/fork_choice_generated/model/minizinc/Block_tree.mzn b/tests/generators/fork_choice_generated/model/minizinc/Block_tree.mzn index 6a3993c99c..ee54f9e534 100644 --- a/tests/generators/fork_choice_generated/model/minizinc/Block_tree.mzn +++ b/tests/generators/fork_choice_generated/model/minizinc/Block_tree.mzn @@ -1,7 +1,7 @@ include "globals.mzn"; int: NB; % num of blocks -int: MD; % max depth +% int: MD; % max depth % int: MW; % max width int: MC; % max children @@ -9,18 +9,18 @@ set of int: BLOCKS = 0..(NB-1); array[BLOCKS] of var BLOCKS: parent; % array[BLOCKS] of var set of BLOCKS: children; -array[BLOCKS] of var int: depths; +% array[BLOCKS] of var int: depths; % array[BLOCKS] of var int: widths; function var int: count_children(var BLOCKS: block) = sum(ch in BLOCKS)(if ch != 0 /\ parent[ch] == block then 1 else 0 endif); -function var int: sub_tree_size(var BLOCKS: block) = - if count_children(block) == 0 then - 1 - else - sum(ch in BLOCKS)(if ch != 0 /\ parent[ch] == block then sub_tree_size(ch) else 0 endif) - endif; +% function var int: sub_tree_size(var BLOCKS: block) = +% if count_children(block) == 0 then +% 1 +% else +% sum(ch in BLOCKS)(if ch != 0 /\ parent[ch] == block then sub_tree_size(ch) else 0 endif) +% endif; % array[BLOCKS] of var int: tst; @@ -29,10 +29,10 @@ function var int: sub_tree_size(var BLOCKS: block) = % constraint forall(b in BLOCKS)(tst[b] == count_children(b)); % constraint forall(b in BLOCKS)(tst2[b] == card(children[b])); % constraint int_set_channel(parent, children); -constraint forall(b in BLOCKS)(if b == 0 then depths[b] == 0 else depths[b] == depths[parent[b]] + 1 endif); +% constraint forall(b in BLOCKS)(if b == 0 then depths[b] == 0 else depths[b] == depths[parent[b]] + 1 endif); constraint forall(b in BLOCKS)(if b != 0 then parent[b] < b else parent[b] == b endif); constraint forall(b in BLOCKS)(count_children(b) <= MC); -constraint forall(b in BLOCKS)(depths[b] <= MD); +% constraint forall(b in BLOCKS)(depths[b] <= MD); % constraint forall(b in BLOCKS)(widths[b] == sub_tree_size(b)); % constraint forall(b in BLOCKS)(widths[b] <= MW); \ No newline at end of file diff --git a/tests/generators/fork_choice_generated/test_gen.py b/tests/generators/fork_choice_generated/test_gen.py index c3ce34ab4d..916a1bd306 100644 --- a/tests/generators/fork_choice_generated/test_gen.py +++ b/tests/generators/fork_choice_generated/test_gen.py @@ -84,13 +84,15 @@ with_invalid_messages = params.get('with_invalid_messages', False) if test_type == 'block_tree': - solutions = block_tree_load_solutions(instances_path) + sm_link_solutions = block_tree_load_solutions(instances_path) + block_tree_solutions = [[0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]] providers = block_tree_create_providers(test_name, forks=block_tree_forks, presets=block_tree_presets, debug=args.fc_gen_debug, initial_seed=initial_seed, - solutions=solutions, + sm_link_solutions=sm_link_solutions, + block_tree_solutions=block_tree_solutions, number_of_variations=nr_variations, number_of_mutations=nr_mutations, with_attester_slashings=with_attester_slashings, From 3eda0b78bdef9e06df732c80798f432c12d17553 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Wed, 10 Apr 2024 17:50:20 +0400 Subject: [PATCH 034/111] Added command line args for `with_attester_slashings` and `with_invalid_messages` flags. --- .../generators/fork_choice_generated/main.py | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/tests/generators/fork_choice_generated/main.py b/tests/generators/fork_choice_generated/main.py index 5d86af27d0..d8ad11c9ec 100644 --- a/tests/generators/fork_choice_generated/main.py +++ b/tests/generators/fork_choice_generated/main.py @@ -189,6 +189,22 @@ def test_fn(phase: str, preset: str, seed: int, sm_links, block_parents): required=False, help='Number of mutations per base test case' ) + arg_parser.add_argument( + '--fc-gen-attester-slashings', + dest='fc_gen_attester_slashings', + default=False, + type=bool, + required=False, + help='Pass with_attester_slashings flag' + ) + arg_parser.add_argument( + '--fc-gen-invalid-messages', + dest='fc_gen_invalid_messages', + default=False, + type=bool, + required=False, + help='Pass with_invalid_messages flag' + ) arg_parser.add_argument( '--fc-gen-instances-path', dest='fc_gen_instances_path', @@ -217,6 +233,6 @@ def test_fn(phase: str, preset: str, seed: int, sm_links, block_parents): block_tree_solutions=block_tree_solutions, number_of_variations=args.fc_gen_variations, number_of_mutations=args.fc_gen_mutations, - with_attester_slashings=True, - with_invalid_messages=True), + with_attester_slashings=args.fc_gen_attester_slashings, + with_invalid_messages=args.fc_gen_invalid_messages), arg_parser) From 67138ef620030f6bba810d0c3bdd71520fc20a68 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 10 Apr 2024 21:01:08 +0600 Subject: [PATCH 035/111] Refactor block parents parametrization --- .../generators/fork_choice_generated/main.py | 19 +++++++++++-------- .../fork_choice_generated/test_gen.py | 8 ++++++-- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/tests/generators/fork_choice_generated/main.py b/tests/generators/fork_choice_generated/main.py index d8ad11c9ec..28cdd7dc5b 100644 --- a/tests/generators/fork_choice_generated/main.py +++ b/tests/generators/fork_choice_generated/main.py @@ -54,7 +54,7 @@ def _find_sm_link_solutions(anchor_epoch: int, def _find_block_tree_solutions(number_of_blocks: int, max_children: int, - number_of_solutions: int) -> Iterable[Iterable[int]]: + number_of_solutions: int) -> []: model = Model('./model/minizinc/Block_tree.mzn') solver = Solver.lookup("gecode") instance = Instance(solver, model) @@ -77,8 +77,7 @@ def _create_providers(test_name: str, /, presets: Iterable[PresetBaseName], debug: bool, initial_seed: int, - sm_link_solutions: Iterable[Iterable[tuple]], - block_tree_solutions: [[int]], + solutions: Iterable, number_of_variations: int, number_of_mutations: int, with_attester_slashings: bool, @@ -108,15 +107,15 @@ def test_fn(phase: str, preset: str, seed: int, sm_links, block_parents): seeds = [rnd.randint(1, 10000) for _ in range(number_of_variations)] seeds[0] = initial_seed - for i, sm_links in enumerate(sm_link_solutions): - block_parents = block_tree_solutions[i % (len(block_tree_solutions) - 1)] + for i, solution in enumerate(solutions): for seed in seeds: for fork_name in forks: for preset_name in presets: spec = spec_targets[preset_name][fork_name] mutation_generator = MutatorsGenerator( spec, seed, number_of_mutations, - lambda: test_fn(fork_name, preset_name, seed, sm_links, block_parents), + lambda: test_fn(fork_name, preset_name, seed, + sm_links=solution['sm_links'], block_parents=solution['block_parents']), debug=debug) for j in range(1 + number_of_mutations): yield TestCase(fork_name=fork_name, @@ -223,14 +222,18 @@ def test_fn(phase: str, preset: str, seed: int, sm_links, block_parents): sm_link_solutions = _find_sm_link_solutions(args.fc_gen_anchor_epoch, args.fc_gen_epochs, args.fc_gen_links) block_tree_solutions = _find_block_tree_solutions(16, 3, 3) + solutions = [] + for index, sm_links in enumerate(sm_link_solutions): + solutions.append({'sm_links': sm_links, + 'block_parents': block_tree_solutions[index % len(block_tree_solutions)]}) + gen_runner.run_generator(GENERATOR_NAME, _create_providers('sm_links_tree_model', forks=forks, presets=presets, debug=args.fc_gen_debug, initial_seed=args.fc_gen_seed, - sm_link_solutions=sm_link_solutions, - block_tree_solutions=block_tree_solutions, + solutions=solutions, number_of_variations=args.fc_gen_variations, number_of_mutations=args.fc_gen_mutations, with_attester_slashings=args.fc_gen_attester_slashings, diff --git a/tests/generators/fork_choice_generated/test_gen.py b/tests/generators/fork_choice_generated/test_gen.py index 916a1bd306..592b70c283 100644 --- a/tests/generators/fork_choice_generated/test_gen.py +++ b/tests/generators/fork_choice_generated/test_gen.py @@ -86,13 +86,17 @@ if test_type == 'block_tree': sm_link_solutions = block_tree_load_solutions(instances_path) block_tree_solutions = [[0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]] + solutions = [] + for index, sm_links in enumerate(sm_link_solutions): + solutions.append({'sm_links': sm_links, + 'block_parents': block_tree_solutions[index % len(block_tree_solutions)]}) + providers = block_tree_create_providers(test_name, forks=block_tree_forks, presets=block_tree_presets, debug=args.fc_gen_debug, initial_seed=initial_seed, - sm_link_solutions=sm_link_solutions, - block_tree_solutions=block_tree_solutions, + solutions=solutions, number_of_variations=nr_variations, number_of_mutations=nr_mutations, with_attester_slashings=with_attester_slashings, From 54756e970bff5bb69bb42e7df2c87f48432204e8 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Thu, 11 Apr 2024 01:29:36 +0400 Subject: [PATCH 036/111] Modified solutions construction --- .../generators/fork_choice_generated/main.py | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/tests/generators/fork_choice_generated/main.py b/tests/generators/fork_choice_generated/main.py index 28cdd7dc5b..7c41ec99b6 100644 --- a/tests/generators/fork_choice_generated/main.py +++ b/tests/generators/fork_choice_generated/main.py @@ -3,6 +3,8 @@ from eth2spec.test.helpers.constants import MINIMAL from eth2spec.test.helpers.specs import spec_targets from eth2spec.gen_helpers.gen_base.gen_typing import TestCase, TestProvider +from itertools import product +from toolz.dicttoolz import merge from typing import Iterable from importlib import import_module from eth2spec.utils import bls @@ -49,12 +51,12 @@ def _find_sm_link_solutions(anchor_epoch: int, solutions = instance.solve(all_solutions=True) for i in range(len(solutions)): - yield [_ for _ in zip(solutions[i, 'sources'], solutions[i, 'targets'])] + yield {'sm_links': list(zip(solutions[i, 'sources'], solutions[i, 'targets']))} def _find_block_tree_solutions(number_of_blocks: int, max_children: int, - number_of_solutions: int) -> []: + number_of_solutions: int) -> Iterable[dict]: model = Model('./model/minizinc/Block_tree.mzn') solver = Solver.lookup("gecode") instance = Instance(solver, model) @@ -62,14 +64,12 @@ def _find_block_tree_solutions(number_of_blocks: int, instance['MC'] = max_children solutions = instance.solve(nr_solutions=number_of_solutions) - return [s.parent for s in solutions] + return [{'block_parents': s.parent} for s in solutions] -def _load_sm_link_solutions(instance_path: str) -> Iterable[Iterable[tuple]]: +def _load_block_tree_instances(instance_path: str) -> Iterable[dict]: solutions = yaml.load(open(instance_path, 'r')) - print('solutions', solutions) - for solution in solutions: - yield list(zip(solution['sources'], solution['targets'])) + return solutions def _create_providers(test_name: str, /, @@ -216,17 +216,12 @@ def test_fn(phase: str, preset: str, seed: int, sm_links, block_parents): args = arg_parser.parse_args() if args.fc_gen_instances_path is not None: - sm_link_solutions = _load_sm_link_solutions(args.fc_gen_instances_path) - block_tree_solutions = _find_block_tree_solutions(16, 3, 3) + solutions = _load_block_tree_instances(args.fc_gen_instances_path) else: sm_link_solutions = _find_sm_link_solutions(args.fc_gen_anchor_epoch, args.fc_gen_epochs, args.fc_gen_links) block_tree_solutions = _find_block_tree_solutions(16, 3, 3) - - solutions = [] - for index, sm_links in enumerate(sm_link_solutions): - solutions.append({'sm_links': sm_links, - 'block_parents': block_tree_solutions[index % len(block_tree_solutions)]}) - + solutions = [merge(*sols) for sols in product(sm_link_solutions, block_tree_solutions)] + gen_runner.run_generator(GENERATOR_NAME, _create_providers('sm_links_tree_model', forks=forks, From f4886e0060b872d8ada4c33a504b4e8cfbb07b18 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Thu, 11 Apr 2024 01:55:43 +0400 Subject: [PATCH 037/111] Changed block_tree format --- .../sample_block_tree.yaml | 98 ++++++++++++++----- 1 file changed, 72 insertions(+), 26 deletions(-) diff --git a/tests/generators/fork_choice_generated/sample_block_tree.yaml b/tests/generators/fork_choice_generated/sample_block_tree.yaml index 488d597379..49f0fc9d63 100644 --- a/tests/generators/fork_choice_generated/sample_block_tree.yaml +++ b/tests/generators/fork_choice_generated/sample_block_tree.yaml @@ -1,26 +1,72 @@ -- sources: [0] - targets: [1] -- sources: [0, 0, 0] - targets: [1, 2, 3] -- sources: [0, 0, 1] - targets: [1, 2, 3] -- sources: [0, 0, 2] - targets: [1, 2, 3] -- sources: [0, 0, 0, 0] - targets: [1, 2, 3, 4] -- sources: [0, 0, 0, 1] - targets: [1, 2, 3, 4] -- sources: [0, 0, 0, 2] - targets: [1, 2, 3, 4] -- sources: [0, 0, 0, 3] - targets: [1, 2, 3, 4] -- sources: [0, 0, 1, 1] - targets: [1, 2, 3, 4] -- sources: [0, 0, 1, 2] - targets: [1, 2, 3, 4] -- sources: [0, 0, 1, 3] - targets: [1, 2, 3, 4] -- sources: [0, 0, 2, 2] - targets: [1, 2, 3, 4] -- sources: [0, 0, 2, 3] - targets: [1, 2, 3, 4] +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] + sm_links: + - [0, 1] +- block_parents: &id001 [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] + sm_links: + - [0, 1] + - [0, 2] + - [0, 3] +- block_parents: *id001 + sm_links: + - [0, 1] + - [0, 2] + - [1, 3] +- block_parents: *id001 + sm_links: + - [0, 1] + - [0, 2] + - [2, 3] +- block_parents: &id002 [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] + sm_links: + - [0, 1] + - [0, 2] + - [0, 3] + - [0, 4] +- block_parents: *id002 + sm_links: + - [0, 1] + - [0, 2] + - [0, 3] + - [1, 4] +- block_parents: *id002 + sm_links: + - [0, 1] + - [0, 2] + - [0, 3] + - [2, 4] +- block_parents: *id002 + sm_links: + - [0, 1] + - [0, 2] + - [0, 3] + - [3, 4] +- block_parents: *id002 + sm_links: + - [0, 1] + - [0, 2] + - [1, 3] + - [1, 4] +- block_parents: *id002 + sm_links: + - [0, 1] + - [0, 2] + - [1, 3] + - [2, 4] +- block_parents: *id002 + sm_links: + - [0, 1] + - [0, 2] + - [1, 3] + - [3, 4] +- block_parents: *id002 + sm_links: + - [0, 1] + - [0, 2] + - [2, 3] + - [2, 4] +- block_parents: *id002 + sm_links: + - [0, 1] + - [0, 2] + - [2, 3] + - [3, 4] From 40588567574d7c10492bf6425e3d9aabd19be864 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Thu, 11 Apr 2024 02:24:51 +0400 Subject: [PATCH 038/111] Added test instances generator (from MiniZinc models to yaml) --- .../generate_test_instances.py | 182 ++++++++++++++++++ .../sample_attester_slashings.yaml | 153 +++++++++++++++ .../sample_block_cover.yaml | 162 ++++++++++++++++ .../sample_block_tree.yaml | 171 +++++++++++++--- .../sample_invalid_messages.yaml | 153 +++++++++++++++ 5 files changed, 795 insertions(+), 26 deletions(-) create mode 100644 tests/generators/fork_choice_generated/generate_test_instances.py create mode 100644 tests/generators/fork_choice_generated/sample_attester_slashings.yaml create mode 100644 tests/generators/fork_choice_generated/sample_block_cover.yaml create mode 100644 tests/generators/fork_choice_generated/sample_invalid_messages.yaml diff --git a/tests/generators/fork_choice_generated/generate_test_instances.py b/tests/generators/fork_choice_generated/generate_test_instances.py new file mode 100644 index 0000000000..2819a1dd08 --- /dev/null +++ b/tests/generators/fork_choice_generated/generate_test_instances.py @@ -0,0 +1,182 @@ +from dataclasses import dataclass, field +from itertools import product +from typing import Iterable +from toolz.dicttoolz import merge +from minizinc import Instance, Model, Solver +from ruamel.yaml import YAML +from typing_extensions import TypedDict + + +def solve_sm_links(anchor_epoch: int, number_of_epochs: int, number_of_links: int, number_of_solutions: int): + sm_links = Model('./model/minizinc/SM_links.mzn') + solver = Solver.lookup("gecode") + instance = Instance(solver, sm_links) + instance['AE'] = anchor_epoch # anchor epoch + instance['NE'] = number_of_epochs # number of epochs, starting from AE + instance['NL'] = number_of_links # number of super-majority links + + assert number_of_solutions is None + solutions = instance.solve(all_solutions=True) + + for i in range(len(solutions)): + yield {'sm_links': list(zip(solutions[i, 'sources'], solutions[i, 'targets']))} + + +def generate_sm_links(params): + anchor_epoch = params['anchor_epoch'] + number_of_epochs = params['number_of_epochs'] + number_of_links = params['number_of_links'] + number_of_solutions = params.get('number_of_solutions') + yield from solve_sm_links(anchor_epoch, number_of_epochs, number_of_links, number_of_solutions) + + +def solve_block_tree(number_of_blocks: int, + max_children: int, + number_of_solutions: int) -> Iterable[dict]: + model = Model('./model/minizinc/Block_tree.mzn') + solver = Solver.lookup("gecode") + instance = Instance(solver, model) + instance['NB'] = number_of_blocks + instance['MC'] = max_children + + if number_of_solutions is None: + solutions = instance.solve(all_solutions=True) + else: + solutions = instance.solve(nr_solutions=number_of_solutions) + + return [{'block_parents': s.parent} for s in solutions] + + +def generate_block_tree(params) -> Iterable[dict]: + number_of_blocks = params['number_of_blocks'] + max_children = params['max_children'] + number_of_solutions = params.get('number_of_solutions') + yield from solve_block_tree(number_of_blocks, max_children, number_of_solutions) + + +def solve_block_cover(anchor_epoch: int, + store_justified_epoch_equal_zero: bool, + block_voting_source_epoch_equal_store_justified_epoch: bool, + block_voting_source_epoch_plus_two_greater_or_equal_current_epoch: bool, + block_is_leaf: bool, + number_of_solutions: int): + block_cover3 = Model('./model/minizinc/Block_cover3.mzn') + solver = Solver.lookup("gecode") + instance = Instance(solver, block_cover3) + instance['AE'] = anchor_epoch + instance['store_je_eq_zero'] = store_justified_epoch_equal_zero + instance['block_vse_eq_store_je'] = block_voting_source_epoch_equal_store_justified_epoch + instance['block_vse_plus_two_ge_curr_e'] = block_voting_source_epoch_plus_two_greater_or_equal_current_epoch + instance['block_is_leaf'] = block_is_leaf + + assert number_of_solutions is not None + result = instance.solve(nr_solutions=number_of_solutions) + + for s in result.solution: + max_block = s.max_block + yield {'block_epochs': s.es[:max_block + 1], + 'parents': s.parents[:max_block + 1], + 'previous_justifications': s.prevs[:max_block + 1], + 'current_justifications': s.currs[:max_block + 1], + 'current_epoch': s.curr_e, + 'store_justified_epoch': s.store_je, + 'target_block': s.target_block, + 'predicates': { + 'store_je_eq_zero': store_justified_epoch_equal_zero, + 'block_vse_eq_store_je': block_voting_source_epoch_equal_store_justified_epoch, + 'block_vse_plus_two_ge_curr_e': block_voting_source_epoch_plus_two_greater_or_equal_current_epoch, + 'block_is_leaf': block_is_leaf + }} + + +def generate_block_cover(params): + anchor_epoch = params['anchor_epoch'] + number_of_solutions = params.get('number_of_solutions', 1) + + for ps in product(*([(True, False)] * 4)): + yield from solve_block_cover(anchor_epoch, *ps, number_of_solutions) + + +# models = { +# 'sm_links': ModelKind('SMLinks', './model/minizinc/SM_links.mzn', {'AE': int, 'NE': int, 'NL': int}), +# 'block_cover': ModelKind('BlockCover', './model/minizinc/Block_cover3.mzn', +# { +# 'AE': int, +# 'store_je_eq_zero': bool, +# 'block_vse_eq_store_je': bool, +# 'block_vse_plus_two_ge_curr_e': bool, +# 'block_is_leaf': bool, +# 'block_is_justified_descendant': bool +# }) +# } + +gen_params = { + 'block_tree_test': { + 'out_path': 'block_tree.yaml', + 'models': ['sm_link', 'block_tree'], + 'params': [ + ({'anchor_epoch': 0, 'number_of_epochs': 4, 'number_of_links': 2}, {'number_of_blocks': 16, 'max_children': 3, 'number_of_solutions': 4}), + ({'anchor_epoch': 0, 'number_of_epochs': 4, 'number_of_links': 3}, {'number_of_blocks': 4, 'max_children': 3, 'number_of_solutions': 4}), + ({'anchor_epoch': 0, 'number_of_epochs': 5, 'number_of_links': 4}, {'number_of_blocks': 4, 'max_children': 3, 'number_of_solutions': 4}), + ] + }, + 'attester_slashings_test': { + 'out_path': 'attester_slashings.yaml', + 'models': ['sm_link', 'block_tree'], + 'params': [ + ([{'sources': [0, 0, 2, 3], 'targets': [1, 2, 3, 4]}], {'number_of_blocks': 16, 'max_children': 3, 'number_of_solutions': 4}), + ({'anchor_epoch': 0, 'number_of_epochs': 4, 'number_of_links': 3}, {'number_of_blocks': 4, 'max_children': 3, 'number_of_solutions': 4}), + ({'anchor_epoch': 0, 'number_of_epochs': 5, 'number_of_links': 4}, {'number_of_blocks': 4, 'max_children': 3, 'number_of_solutions': 4}), + ] + }, + 'invalid_messages_test': { + 'out_path': 'invalid_messages.yaml', + 'models': ['sm_link', 'block_tree'], + 'params': [ + ([{'sources': [0, 0, 2, 3], 'targets': [1, 2, 3, 4]}], {'number_of_blocks': 16, 'max_children': 3, 'number_of_solutions': 4}), + ({'anchor_epoch': 0, 'number_of_epochs': 4, 'number_of_links': 3}, {'number_of_blocks': 4, 'max_children': 3, 'number_of_solutions': 4}), + ({'anchor_epoch': 0, 'number_of_epochs': 5, 'number_of_links': 4}, {'number_of_blocks': 4, 'max_children': 3, 'number_of_solutions': 4}), + ] + }, + 'block_cover': { + 'out_path': 'block_cover.yaml', + 'models': ['block_cover'], + 'params': [ + ({'anchor_epoch': 0, 'number_of_solutions': 1},), + ({'anchor_epoch': 2, 'number_of_solutions': 1},), + ] + } +} + + +if __name__ == '__main__': + yaml = YAML(typ='safe') + sm_links = [] + + for model_name, parameters in gen_params.items(): + print(f'processing {model_name}') + out_path = parameters['out_path'] + models = parameters['models'] + solutions = [] + for params in parameters['params']: + model_solutions = [] + for model, mod_params in zip(models, params): + print(f' model: {model}') + print(f' parameters: {mod_params}') + if isinstance(mod_params, list): + model_solutions.append(mod_params) + elif isinstance(mod_params, dict): + if model == 'sm_link': + model_solutions.append(list(generate_sm_links(mod_params))) + elif model == 'block_tree': + model_solutions.append(list(generate_block_tree(mod_params))) + elif model == 'block_cover': + model_solutions.append(list(generate_block_cover(mod_params))) + else: + print('todo', model, mod_params) + else: + assert False + results = [merge(*sol) for sol in product(*model_solutions)] + solutions.extend(results) + with open(out_path, 'w') as f: + yaml.dump(solutions, f) diff --git a/tests/generators/fork_choice_generated/sample_attester_slashings.yaml b/tests/generators/fork_choice_generated/sample_attester_slashings.yaml new file mode 100644 index 0000000000..af7cfd9658 --- /dev/null +++ b/tests/generators/fork_choice_generated/sample_attester_slashings.yaml @@ -0,0 +1,153 @@ +- block_parents: [0, 0, 1, 2, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0] + sources: &id001 [0, 0, 2, 3] + targets: &id002 [1, 2, 3, 4] +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 3, 3, 2, 2, 1, 1, 0, 0] + sources: *id001 + targets: *id002 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 3, 3, 2, 2, 1, 1, 0, 0] + sources: *id001 + targets: *id002 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 3, 2, 2, 1, 1, 0, 0] + sources: *id001 + targets: *id002 +- block_parents: &id004 [0, 0, 0, 0] + sm_links: &id003 + - [0, 1] + - [0, 2] + - [0, 3] +- block_parents: &id005 [0, 0, 1, 0] + sm_links: *id003 +- block_parents: &id007 [0, 0, 0, 1] + sm_links: *id003 +- block_parents: &id008 [0, 0, 1, 1] + sm_links: *id003 +- block_parents: *id004 + sm_links: &id006 + - [0, 1] + - [0, 2] + - [1, 3] +- block_parents: *id005 + sm_links: *id006 +- block_parents: *id007 + sm_links: *id006 +- block_parents: *id008 + sm_links: *id006 +- block_parents: *id004 + sm_links: &id009 + - [0, 1] + - [0, 2] + - [2, 3] +- block_parents: *id005 + sm_links: *id009 +- block_parents: *id007 + sm_links: *id009 +- block_parents: *id008 + sm_links: *id009 +- block_parents: &id011 [0, 0, 0, 0] + sm_links: &id010 + - [0, 1] + - [0, 2] + - [0, 3] + - [0, 4] +- block_parents: &id012 [0, 0, 1, 0] + sm_links: *id010 +- block_parents: &id014 [0, 0, 0, 1] + sm_links: *id010 +- block_parents: &id015 [0, 0, 1, 1] + sm_links: *id010 +- block_parents: *id011 + sm_links: &id013 + - [0, 1] + - [0, 2] + - [0, 3] + - [1, 4] +- block_parents: *id012 + sm_links: *id013 +- block_parents: *id014 + sm_links: *id013 +- block_parents: *id015 + sm_links: *id013 +- block_parents: *id011 + sm_links: &id016 + - [0, 1] + - [0, 2] + - [0, 3] + - [2, 4] +- block_parents: *id012 + sm_links: *id016 +- block_parents: *id014 + sm_links: *id016 +- block_parents: *id015 + sm_links: *id016 +- block_parents: *id011 + sm_links: &id017 + - [0, 1] + - [0, 2] + - [0, 3] + - [3, 4] +- block_parents: *id012 + sm_links: *id017 +- block_parents: *id014 + sm_links: *id017 +- block_parents: *id015 + sm_links: *id017 +- block_parents: *id011 + sm_links: &id018 + - [0, 1] + - [0, 2] + - [1, 3] + - [1, 4] +- block_parents: *id012 + sm_links: *id018 +- block_parents: *id014 + sm_links: *id018 +- block_parents: *id015 + sm_links: *id018 +- block_parents: *id011 + sm_links: &id019 + - [0, 1] + - [0, 2] + - [1, 3] + - [2, 4] +- block_parents: *id012 + sm_links: *id019 +- block_parents: *id014 + sm_links: *id019 +- block_parents: *id015 + sm_links: *id019 +- block_parents: *id011 + sm_links: &id020 + - [0, 1] + - [0, 2] + - [1, 3] + - [3, 4] +- block_parents: *id012 + sm_links: *id020 +- block_parents: *id014 + sm_links: *id020 +- block_parents: *id015 + sm_links: *id020 +- block_parents: *id011 + sm_links: &id021 + - [0, 1] + - [0, 2] + - [2, 3] + - [2, 4] +- block_parents: *id012 + sm_links: *id021 +- block_parents: *id014 + sm_links: *id021 +- block_parents: *id015 + sm_links: *id021 +- block_parents: *id011 + sm_links: &id022 + - [0, 1] + - [0, 2] + - [2, 3] + - [3, 4] +- block_parents: *id012 + sm_links: *id022 +- block_parents: *id014 + sm_links: *id022 +- block_parents: *id015 + sm_links: *id022 diff --git a/tests/generators/fork_choice_generated/sample_block_cover.yaml b/tests/generators/fork_choice_generated/sample_block_cover.yaml new file mode 100644 index 0000000000..d04d3b1408 --- /dev/null +++ b/tests/generators/fork_choice_generated/sample_block_cover.yaml @@ -0,0 +1,162 @@ +- block_epochs: [0] + current_epoch: 1 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: true} + previous_justifications: [false] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [0, 1] + current_epoch: 1 + current_justifications: [false, false] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: true} + previous_justifications: [false, false] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [0] + current_epoch: 3 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: true} + previous_justifications: [false] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [0, 1] + current_epoch: 3 + current_justifications: [false, false] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: true} + previous_justifications: [false, false] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [0, 1, 1] + current_epoch: 2 + current_justifications: [false, false, true] + parents: [0, 0, 0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false, false] + store_justified_epoch: 1 + target_block: 2 +- block_epochs: [0, 1, 1, 2, 2] + current_epoch: 2 + current_justifications: [false, true, false, false, false] + parents: [0, 0, 0, 1, 1] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false, false, false, false] + store_justified_epoch: 1 + target_block: 1 +- block_epochs: [0, 1, 1] + current_epoch: 2 + current_justifications: [false, false, true] + parents: [0, 0, 0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false, false] + store_justified_epoch: 1 + target_block: 1 +- block_epochs: [0, 1] + current_epoch: 2 + current_justifications: [false, true] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false] + store_justified_epoch: 1 + target_block: 0 +- block_epochs: [0, 1, 1] + current_epoch: 3 + current_justifications: [false, false, true] + parents: [0, 0, 0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, false, false] + store_justified_epoch: 1 + target_block: 1 +- block_epochs: [0, 1] + current_epoch: 3 + current_justifications: [false, true] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, false] + store_justified_epoch: 1 + target_block: 0 +- block_epochs: [2] + current_epoch: 3 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2, 3] + current_epoch: 3 + current_justifications: [false, false] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2] + current_epoch: 5 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2, 3] + current_epoch: 5 + current_justifications: [false, false] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2, 3, 3] + current_epoch: 4 + current_justifications: [false, false, true] + parents: [0, 0, 0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false, false] + store_justified_epoch: 3 + target_block: 1 +- block_epochs: [2, 3] + current_epoch: 4 + current_justifications: [false, true] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false] + store_justified_epoch: 3 + target_block: 0 +- block_epochs: [2, 3, 3] + current_epoch: 5 + current_justifications: [false, false, true] + parents: [0, 0, 0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, false, false] + store_justified_epoch: 3 + target_block: 1 +- block_epochs: [2, 3] + current_epoch: 5 + current_justifications: [false, true] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, false] + store_justified_epoch: 3 + target_block: 0 diff --git a/tests/generators/fork_choice_generated/sample_block_tree.yaml b/tests/generators/fork_choice_generated/sample_block_tree.yaml index 49f0fc9d63..1ac1eafb79 100644 --- a/tests/generators/fork_choice_generated/sample_block_tree.yaml +++ b/tests/generators/fork_choice_generated/sample_block_tree.yaml @@ -1,72 +1,191 @@ -- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] - sm_links: +- block_parents: &id002 [0, 0, 1, 2, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0] + sm_links: &id001 - [0, 1] -- block_parents: &id001 [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] - sm_links: + - [0, 2] +- block_parents: &id003 [0, 0, 1, 2, 3, 4, 5, 4, 3, 3, 2, 2, 1, 1, 0, 0] + sm_links: *id001 +- block_parents: &id005 [0, 0, 1, 2, 3, 4, 4, 5, 3, 3, 2, 2, 1, 1, 0, 0] + sm_links: *id001 +- block_parents: &id006 [0, 0, 1, 2, 3, 4, 5, 5, 3, 3, 2, 2, 1, 1, 0, 0] + sm_links: *id001 +- block_parents: *id002 + sm_links: &id004 - [0, 1] + - [0, 3] +- block_parents: *id003 + sm_links: *id004 +- block_parents: *id005 + sm_links: *id004 +- block_parents: *id006 + sm_links: *id004 +- block_parents: *id002 + sm_links: &id007 - [0, 2] - [0, 3] -- block_parents: *id001 - sm_links: +- block_parents: *id003 + sm_links: *id007 +- block_parents: *id005 + sm_links: *id007 +- block_parents: *id006 + sm_links: *id007 +- block_parents: *id002 + sm_links: &id008 + - [0, 1] + - [1, 3] +- block_parents: *id003 + sm_links: *id008 +- block_parents: *id005 + sm_links: *id008 +- block_parents: *id006 + sm_links: *id008 +- block_parents: *id002 + sm_links: &id009 + - [0, 2] + - [2, 3] +- block_parents: *id003 + sm_links: *id009 +- block_parents: *id005 + sm_links: *id009 +- block_parents: *id006 + sm_links: *id009 +- block_parents: &id011 [0, 0, 0, 0] + sm_links: &id010 + - [0, 1] + - [0, 2] + - [0, 3] +- block_parents: &id012 [0, 0, 1, 0] + sm_links: *id010 +- block_parents: &id014 [0, 0, 0, 1] + sm_links: *id010 +- block_parents: &id015 [0, 0, 1, 1] + sm_links: *id010 +- block_parents: *id011 + sm_links: &id013 - [0, 1] - [0, 2] - [1, 3] -- block_parents: *id001 - sm_links: +- block_parents: *id012 + sm_links: *id013 +- block_parents: *id014 + sm_links: *id013 +- block_parents: *id015 + sm_links: *id013 +- block_parents: *id011 + sm_links: &id016 - [0, 1] - [0, 2] - [2, 3] -- block_parents: &id002 [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] - sm_links: +- block_parents: *id012 + sm_links: *id016 +- block_parents: *id014 + sm_links: *id016 +- block_parents: *id015 + sm_links: *id016 +- block_parents: &id018 [0, 0, 0, 0] + sm_links: &id017 - [0, 1] - [0, 2] - [0, 3] - [0, 4] -- block_parents: *id002 - sm_links: +- block_parents: &id019 [0, 0, 1, 0] + sm_links: *id017 +- block_parents: &id021 [0, 0, 0, 1] + sm_links: *id017 +- block_parents: &id022 [0, 0, 1, 1] + sm_links: *id017 +- block_parents: *id018 + sm_links: &id020 - [0, 1] - [0, 2] - [0, 3] - [1, 4] -- block_parents: *id002 - sm_links: +- block_parents: *id019 + sm_links: *id020 +- block_parents: *id021 + sm_links: *id020 +- block_parents: *id022 + sm_links: *id020 +- block_parents: *id018 + sm_links: &id023 - [0, 1] - [0, 2] - [0, 3] - [2, 4] -- block_parents: *id002 - sm_links: +- block_parents: *id019 + sm_links: *id023 +- block_parents: *id021 + sm_links: *id023 +- block_parents: *id022 + sm_links: *id023 +- block_parents: *id018 + sm_links: &id024 - [0, 1] - [0, 2] - [0, 3] - [3, 4] -- block_parents: *id002 - sm_links: +- block_parents: *id019 + sm_links: *id024 +- block_parents: *id021 + sm_links: *id024 +- block_parents: *id022 + sm_links: *id024 +- block_parents: *id018 + sm_links: &id025 - [0, 1] - [0, 2] - [1, 3] - [1, 4] -- block_parents: *id002 - sm_links: +- block_parents: *id019 + sm_links: *id025 +- block_parents: *id021 + sm_links: *id025 +- block_parents: *id022 + sm_links: *id025 +- block_parents: *id018 + sm_links: &id026 - [0, 1] - [0, 2] - [1, 3] - [2, 4] -- block_parents: *id002 - sm_links: +- block_parents: *id019 + sm_links: *id026 +- block_parents: *id021 + sm_links: *id026 +- block_parents: *id022 + sm_links: *id026 +- block_parents: *id018 + sm_links: &id027 - [0, 1] - [0, 2] - [1, 3] - [3, 4] -- block_parents: *id002 - sm_links: +- block_parents: *id019 + sm_links: *id027 +- block_parents: *id021 + sm_links: *id027 +- block_parents: *id022 + sm_links: *id027 +- block_parents: *id018 + sm_links: &id028 - [0, 1] - [0, 2] - [2, 3] - [2, 4] -- block_parents: *id002 - sm_links: +- block_parents: *id019 + sm_links: *id028 +- block_parents: *id021 + sm_links: *id028 +- block_parents: *id022 + sm_links: *id028 +- block_parents: *id018 + sm_links: &id029 - [0, 1] - [0, 2] - [2, 3] - [3, 4] +- block_parents: *id019 + sm_links: *id029 +- block_parents: *id021 + sm_links: *id029 +- block_parents: *id022 + sm_links: *id029 diff --git a/tests/generators/fork_choice_generated/sample_invalid_messages.yaml b/tests/generators/fork_choice_generated/sample_invalid_messages.yaml new file mode 100644 index 0000000000..af7cfd9658 --- /dev/null +++ b/tests/generators/fork_choice_generated/sample_invalid_messages.yaml @@ -0,0 +1,153 @@ +- block_parents: [0, 0, 1, 2, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0] + sources: &id001 [0, 0, 2, 3] + targets: &id002 [1, 2, 3, 4] +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 3, 3, 2, 2, 1, 1, 0, 0] + sources: *id001 + targets: *id002 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 3, 3, 2, 2, 1, 1, 0, 0] + sources: *id001 + targets: *id002 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 3, 2, 2, 1, 1, 0, 0] + sources: *id001 + targets: *id002 +- block_parents: &id004 [0, 0, 0, 0] + sm_links: &id003 + - [0, 1] + - [0, 2] + - [0, 3] +- block_parents: &id005 [0, 0, 1, 0] + sm_links: *id003 +- block_parents: &id007 [0, 0, 0, 1] + sm_links: *id003 +- block_parents: &id008 [0, 0, 1, 1] + sm_links: *id003 +- block_parents: *id004 + sm_links: &id006 + - [0, 1] + - [0, 2] + - [1, 3] +- block_parents: *id005 + sm_links: *id006 +- block_parents: *id007 + sm_links: *id006 +- block_parents: *id008 + sm_links: *id006 +- block_parents: *id004 + sm_links: &id009 + - [0, 1] + - [0, 2] + - [2, 3] +- block_parents: *id005 + sm_links: *id009 +- block_parents: *id007 + sm_links: *id009 +- block_parents: *id008 + sm_links: *id009 +- block_parents: &id011 [0, 0, 0, 0] + sm_links: &id010 + - [0, 1] + - [0, 2] + - [0, 3] + - [0, 4] +- block_parents: &id012 [0, 0, 1, 0] + sm_links: *id010 +- block_parents: &id014 [0, 0, 0, 1] + sm_links: *id010 +- block_parents: &id015 [0, 0, 1, 1] + sm_links: *id010 +- block_parents: *id011 + sm_links: &id013 + - [0, 1] + - [0, 2] + - [0, 3] + - [1, 4] +- block_parents: *id012 + sm_links: *id013 +- block_parents: *id014 + sm_links: *id013 +- block_parents: *id015 + sm_links: *id013 +- block_parents: *id011 + sm_links: &id016 + - [0, 1] + - [0, 2] + - [0, 3] + - [2, 4] +- block_parents: *id012 + sm_links: *id016 +- block_parents: *id014 + sm_links: *id016 +- block_parents: *id015 + sm_links: *id016 +- block_parents: *id011 + sm_links: &id017 + - [0, 1] + - [0, 2] + - [0, 3] + - [3, 4] +- block_parents: *id012 + sm_links: *id017 +- block_parents: *id014 + sm_links: *id017 +- block_parents: *id015 + sm_links: *id017 +- block_parents: *id011 + sm_links: &id018 + - [0, 1] + - [0, 2] + - [1, 3] + - [1, 4] +- block_parents: *id012 + sm_links: *id018 +- block_parents: *id014 + sm_links: *id018 +- block_parents: *id015 + sm_links: *id018 +- block_parents: *id011 + sm_links: &id019 + - [0, 1] + - [0, 2] + - [1, 3] + - [2, 4] +- block_parents: *id012 + sm_links: *id019 +- block_parents: *id014 + sm_links: *id019 +- block_parents: *id015 + sm_links: *id019 +- block_parents: *id011 + sm_links: &id020 + - [0, 1] + - [0, 2] + - [1, 3] + - [3, 4] +- block_parents: *id012 + sm_links: *id020 +- block_parents: *id014 + sm_links: *id020 +- block_parents: *id015 + sm_links: *id020 +- block_parents: *id011 + sm_links: &id021 + - [0, 1] + - [0, 2] + - [2, 3] + - [2, 4] +- block_parents: *id012 + sm_links: *id021 +- block_parents: *id014 + sm_links: *id021 +- block_parents: *id015 + sm_links: *id021 +- block_parents: *id011 + sm_links: &id022 + - [0, 1] + - [0, 2] + - [2, 3] + - [3, 4] +- block_parents: *id012 + sm_links: *id022 +- block_parents: *id014 + sm_links: *id022 +- block_parents: *id015 + sm_links: *id022 From 8cc662bdbf21cb21d9ed9bf21a9af238d0e8ece2 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 11 Apr 2024 17:44:24 +0600 Subject: [PATCH 039/111] Add invalid attestations and slashings --- .../fork_choice/test_sm_links_tree_model.py | 75 ++++++++++++------- 1 file changed, 48 insertions(+), 27 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py index 5b664822d8..8540a4f9d8 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py @@ -624,6 +624,14 @@ def _spoil_block(spec, rnd: random.Random, signed_block): signed_block.message.state_root = spec.Root(rnd.randbytes(32)) +def _spoil_attester_slashing(spec, rnd: random.Random, attester_slashing): + attester_slashing.attestation_2.data = attester_slashing.attestation_1.data + + +def _spoil_attestation(spec, rnd: random.Random, attestation): + attestation.data.target.epoch = spec.GENESIS_EPOCH + + def _generate_block_tree(spec, anchor_tip: BranchTip, rnd: random.Random, @@ -656,26 +664,7 @@ def _generate_block_tree(spec, # Produce block proposer = spec.get_beacon_proposer_index(parent_state) - block_should_be_valid = rnd.randint(0, 99) >= INVALID_MESSAGES_RATE if with_invalid_messages else True - - if block_should_be_valid: - signed_block, post_state, in_block_attestations, in_block_attester_slashings = _produce_block( - spec, parent_state, in_block_attestations, in_block_attester_slashings) - - # A block can be unintentionally invalid, e.g. a proposer was slashed - # In this case it is expected that post_state == parent_state, - # and beacon operations returned from _produce_block are expected to remain untouched - block_is_valid = post_state.latest_block_header.slot == signed_block.message.slot - - # Valid block - signed_block_messages.append(ProtocolMessage(signed_block, block_is_valid)) - post_states.append(post_state) - - # Update tips - if block_is_valid: - block_tree_tips.discard(parent_index) - block_tree_tips.add(block_index) - else: + if with_invalid_messages and rnd.randint(0, 99) < INVALID_MESSAGES_RATE: # Intentionally invalid block # Do not update slashings and attestations for them to be included in the future blocks signed_block, _, _, _ = _produce_block( @@ -684,8 +673,25 @@ def _generate_block_tree(spec, signed_block_messages.append(ProtocolMessage(signed_block, False)) # Append the parent state as the post state as if the block were not applied post_states.append(parent_state) + else: + signed_block, post_state, in_block_attestations, in_block_attester_slashings = _produce_block( + spec, parent_state, in_block_attestations, in_block_attester_slashings) + + # A block can be unintentionally invalid, e.g. a proposer was slashed + # In this case it is expected that post_state == parent_state, + # and beacon operations returned from _produce_block are expected to remain untouched + block_is_valid = post_state.latest_block_header.slot == signed_block.message.slot + + # Valid block + signed_block_messages.append(ProtocolMessage(signed_block, block_is_valid)) + post_states.append(post_state) - # Next block + # Update tips + if block_is_valid: + block_tree_tips.discard(parent_index) + block_tree_tips.add(block_index) + + # Next block block_index += 1 # Attest to randomly selected tips @@ -709,7 +715,12 @@ def split_list(lst, n): for a in attestations_in_slot: choice = rnd.randint(0, 99) if choice < OFF_CHAIN_ATTESTATION_RATE: - out_of_block_attestation_messages.append(ProtocolMessage(a, True)) + if with_invalid_messages and rnd.randint(0, 99) < INVALID_MESSAGES_RATE: + _spoil_attestation(spec, rnd, a) + attestation_message = ProtocolMessage(a, False) + else: + attestation_message = ProtocolMessage(a, True) + out_of_block_attestation_messages.append(attestation_message) elif choice < OFF_CHAIN_ATTESTATION_RATE + ON_CHAIN_ATTESTATION_RATE: in_block_attestations.insert(0, a) else: @@ -728,7 +739,12 @@ def split_list(lst, n): choice = rnd.randint(0, 99) if choice < OFF_CHAIN_SLASHING_RATE: - out_of_block_attester_slashing_messages.append(ProtocolMessage(attester_slashing, True)) + if with_invalid_messages and rnd.randint(0, 99) < INVALID_MESSAGES_RATE: + _spoil_attester_slashing(spec, rnd, attester_slashing) + attester_slashing_message = ProtocolMessage(attester_slashing, False) + else: + attester_slashing_message = ProtocolMessage(attester_slashing, True) + out_of_block_attester_slashing_messages.append(attester_slashing_message) elif choice < OFF_CHAIN_SLASHING_RATE + ON_CHAIN_SLASHING_RATE: in_block_attester_slashings.append(attester_slashing) else: @@ -746,10 +762,14 @@ def split_list(lst, n): print(' ', 'state.current_justified_checkpoint:', '(epoch=' + str(post_states[len(post_states) - 1].current_justified_checkpoint.epoch) + ', root=' + str(post_states[len(post_states) - 1].current_justified_checkpoint.root)[:6] + ')') - print('on_attestation: ') + + print('on_block:') + print(' ', 'count =', len(signed_block_messages)) + print(' ', 'valid =', len([b for b in signed_block_messages if b.valid])) + print('on_attestation:') print(' ', 'count =', len(out_of_block_attestation_messages)) print(' ', 'valid =', len([a for a in out_of_block_attestation_messages if a.valid])) - print('on_attester_slashing: ') + print('on_attester_slashing:') print(' ', 'count =', len(out_of_block_attester_slashing_messages)) print(' ', 'valid =', len([s for s in out_of_block_attester_slashing_messages if s.valid])) @@ -856,13 +876,14 @@ def test_sm_links_tree_model(spec, # on_attestation for attestations from the previous slot for attestation_message in (a for a in attestation_messages if a.payload.data.slot == slot - 1): - yield from add_attestation(spec, store, attestation_message.payload, test_steps, attestation_message.valid) + yield from add_attestation(spec, store, attestation_message.payload, + test_steps, valid=attestation_message.valid) # on_attester_slashing for slashing from the previous slot for attester_slashing_message in (s for s in attester_slashing_messages if s.payload.attestation_1.data.slot == slot - 1): yield from add_attester_slashing(spec, store, attester_slashing_message.payload, - test_steps, attester_slashing_message.valid) + test_steps, valid=attester_slashing_message.valid) # on_block for blocks from the current slot for signed_block_message in (b for b in signed_block_messages if b.payload.message.slot == slot): From 12c214e9d0987f966fc655607e9431bc87c20af0 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 11 Apr 2024 20:19:32 +0600 Subject: [PATCH 040/111] Handle slashed proposer as it were a regular invalid block --- .../fork_choice/test_sm_links_tree_model.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py index 8540a4f9d8..8385c5a96a 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py @@ -664,11 +664,11 @@ def _generate_block_tree(spec, # Produce block proposer = spec.get_beacon_proposer_index(parent_state) - if with_invalid_messages and rnd.randint(0, 99) < INVALID_MESSAGES_RATE: - # Intentionally invalid block - # Do not update slashings and attestations for them to be included in the future blocks - signed_block, _, _, _ = _produce_block( - spec, parent_state, in_block_attestations, in_block_attester_slashings) + if parent_state.validators[proposer].slashed or ( + with_invalid_messages and rnd.randint(0, 99) < INVALID_MESSAGES_RATE): + # Do not include attestations and slashings into invalid block + # as clients may opt in to process or not process attestations contained by invalid block + signed_block, _, _, _ = _produce_block(spec, parent_state, [], []) _spoil_block(spec, rnd, signed_block) signed_block_messages.append(ProtocolMessage(signed_block, False)) # Append the parent state as the post state as if the block were not applied @@ -677,13 +677,8 @@ def _generate_block_tree(spec, signed_block, post_state, in_block_attestations, in_block_attester_slashings = _produce_block( spec, parent_state, in_block_attestations, in_block_attester_slashings) - # A block can be unintentionally invalid, e.g. a proposer was slashed - # In this case it is expected that post_state == parent_state, - # and beacon operations returned from _produce_block are expected to remain untouched - block_is_valid = post_state.latest_block_header.slot == signed_block.message.slot - # Valid block - signed_block_messages.append(ProtocolMessage(signed_block, block_is_valid)) + signed_block_messages.append(ProtocolMessage(signed_block, True)) post_states.append(post_state) # Update tips From 573b5efbce3594ada85a82fe4d7b1314053a38c5 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 11 Apr 2024 20:23:43 +0600 Subject: [PATCH 041/111] Fix valid block production --- .../test/phase0/fork_choice/test_sm_links_tree_model.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py index 8385c5a96a..bb68fe759a 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py @@ -682,9 +682,8 @@ def _generate_block_tree(spec, post_states.append(post_state) # Update tips - if block_is_valid: - block_tree_tips.discard(parent_index) - block_tree_tips.add(block_index) + block_tree_tips.discard(parent_index) + block_tree_tips.add(block_index) # Next block block_index += 1 From 1f8041799cfbeed3bce5efea13eb6ec8166ade61 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Fri, 12 Apr 2024 00:51:12 +0400 Subject: [PATCH 042/111] fix solutions construction --- tests/generators/fork_choice_generated/test_gen.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/tests/generators/fork_choice_generated/test_gen.py b/tests/generators/fork_choice_generated/test_gen.py index 592b70c283..40d4bbbe24 100644 --- a/tests/generators/fork_choice_generated/test_gen.py +++ b/tests/generators/fork_choice_generated/test_gen.py @@ -9,7 +9,7 @@ from main import ( forks as block_tree_forks, presets as block_tree_presets, - _load_sm_link_solutions as block_tree_load_solutions, + _load_block_tree_instances as block_tree_load_solutions, _create_providers as block_tree_create_providers ) @@ -84,13 +84,7 @@ with_invalid_messages = params.get('with_invalid_messages', False) if test_type == 'block_tree': - sm_link_solutions = block_tree_load_solutions(instances_path) - block_tree_solutions = [[0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]] - solutions = [] - for index, sm_links in enumerate(sm_link_solutions): - solutions.append({'sm_links': sm_links, - 'block_parents': block_tree_solutions[index % len(block_tree_solutions)]}) - + solutions = block_tree_load_solutions(instances_path) providers = block_tree_create_providers(test_name, forks=block_tree_forks, presets=block_tree_presets, From be1b1f8a7b89115191f9d5db088b1223f9f10e33 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Fri, 12 Apr 2024 00:51:42 +0400 Subject: [PATCH 043/111] fixed file format (`sm_links` instead of `sources/targets`) --- .../sample_attester_slashings.yaml | 174 +++++++++--------- .../sample_invalid_messages.yaml | 174 +++++++++--------- 2 files changed, 174 insertions(+), 174 deletions(-) diff --git a/tests/generators/fork_choice_generated/sample_attester_slashings.yaml b/tests/generators/fork_choice_generated/sample_attester_slashings.yaml index af7cfd9658..a3f0669faa 100644 --- a/tests/generators/fork_choice_generated/sample_attester_slashings.yaml +++ b/tests/generators/fork_choice_generated/sample_attester_slashings.yaml @@ -1,153 +1,153 @@ - block_parents: [0, 0, 1, 2, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0] - sources: &id001 [0, 0, 2, 3] - targets: &id002 [1, 2, 3, 4] + sm_links: &id001 + - [0, 1] + - [0, 2] + - [2, 3] + - [3, 4] - block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 3, 3, 2, 2, 1, 1, 0, 0] - sources: *id001 - targets: *id002 + sm_links: *id001 - block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 3, 3, 2, 2, 1, 1, 0, 0] - sources: *id001 - targets: *id002 + sm_links: *id001 - block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 3, 2, 2, 1, 1, 0, 0] - sources: *id001 - targets: *id002 -- block_parents: &id004 [0, 0, 0, 0] - sm_links: &id003 + sm_links: *id001 +- block_parents: &id003 [0, 0, 0, 0] + sm_links: &id002 - [0, 1] - [0, 2] - [0, 3] -- block_parents: &id005 [0, 0, 1, 0] - sm_links: *id003 -- block_parents: &id007 [0, 0, 0, 1] - sm_links: *id003 -- block_parents: &id008 [0, 0, 1, 1] - sm_links: *id003 -- block_parents: *id004 - sm_links: &id006 +- block_parents: &id004 [0, 0, 1, 0] + sm_links: *id002 +- block_parents: &id006 [0, 0, 0, 1] + sm_links: *id002 +- block_parents: &id007 [0, 0, 1, 1] + sm_links: *id002 +- block_parents: *id003 + sm_links: &id005 - [0, 1] - [0, 2] - [1, 3] -- block_parents: *id005 - sm_links: *id006 +- block_parents: *id004 + sm_links: *id005 +- block_parents: *id006 + sm_links: *id005 - block_parents: *id007 - sm_links: *id006 -- block_parents: *id008 - sm_links: *id006 + sm_links: *id005 +- block_parents: *id003 + sm_links: &id008 + - [0, 1] + - [0, 2] + - [2, 3] - block_parents: *id004 + sm_links: *id008 +- block_parents: *id006 + sm_links: *id008 +- block_parents: *id007 + sm_links: *id008 +- block_parents: &id010 [0, 0, 0, 0] sm_links: &id009 - [0, 1] - [0, 2] - - [2, 3] -- block_parents: *id005 + - [0, 3] + - [0, 4] +- block_parents: &id011 [0, 0, 1, 0] sm_links: *id009 -- block_parents: *id007 +- block_parents: &id013 [0, 0, 0, 1] sm_links: *id009 -- block_parents: *id008 +- block_parents: &id014 [0, 0, 1, 1] sm_links: *id009 -- block_parents: &id011 [0, 0, 0, 0] - sm_links: &id010 +- block_parents: *id010 + sm_links: &id012 - [0, 1] - [0, 2] - [0, 3] - - [0, 4] -- block_parents: &id012 [0, 0, 1, 0] - sm_links: *id010 -- block_parents: &id014 [0, 0, 0, 1] - sm_links: *id010 -- block_parents: &id015 [0, 0, 1, 1] - sm_links: *id010 + - [1, 4] - block_parents: *id011 - sm_links: &id013 + sm_links: *id012 +- block_parents: *id013 + sm_links: *id012 +- block_parents: *id014 + sm_links: *id012 +- block_parents: *id010 + sm_links: &id015 - [0, 1] - [0, 2] - [0, 3] - - [1, 4] -- block_parents: *id012 - sm_links: *id013 -- block_parents: *id014 - sm_links: *id013 -- block_parents: *id015 - sm_links: *id013 + - [2, 4] - block_parents: *id011 + sm_links: *id015 +- block_parents: *id013 + sm_links: *id015 +- block_parents: *id014 + sm_links: *id015 +- block_parents: *id010 sm_links: &id016 - [0, 1] - [0, 2] - [0, 3] - - [2, 4] -- block_parents: *id012 + - [3, 4] +- block_parents: *id011 sm_links: *id016 -- block_parents: *id014 +- block_parents: *id013 sm_links: *id016 -- block_parents: *id015 +- block_parents: *id014 sm_links: *id016 -- block_parents: *id011 +- block_parents: *id010 sm_links: &id017 - [0, 1] - [0, 2] - - [0, 3] - - [3, 4] -- block_parents: *id012 + - [1, 3] + - [1, 4] +- block_parents: *id011 sm_links: *id017 -- block_parents: *id014 +- block_parents: *id013 sm_links: *id017 -- block_parents: *id015 +- block_parents: *id014 sm_links: *id017 -- block_parents: *id011 +- block_parents: *id010 sm_links: &id018 - [0, 1] - [0, 2] - [1, 3] - - [1, 4] -- block_parents: *id012 + - [2, 4] +- block_parents: *id011 sm_links: *id018 -- block_parents: *id014 +- block_parents: *id013 sm_links: *id018 -- block_parents: *id015 +- block_parents: *id014 sm_links: *id018 -- block_parents: *id011 +- block_parents: *id010 sm_links: &id019 - [0, 1] - [0, 2] - [1, 3] - - [2, 4] -- block_parents: *id012 + - [3, 4] +- block_parents: *id011 sm_links: *id019 -- block_parents: *id014 +- block_parents: *id013 sm_links: *id019 -- block_parents: *id015 +- block_parents: *id014 sm_links: *id019 -- block_parents: *id011 +- block_parents: *id010 sm_links: &id020 - [0, 1] - [0, 2] - - [1, 3] - - [3, 4] -- block_parents: *id012 + - [2, 3] + - [2, 4] +- block_parents: *id011 sm_links: *id020 -- block_parents: *id014 +- block_parents: *id013 sm_links: *id020 -- block_parents: *id015 +- block_parents: *id014 sm_links: *id020 -- block_parents: *id011 +- block_parents: *id010 sm_links: &id021 - [0, 1] - [0, 2] - [2, 3] - - [2, 4] -- block_parents: *id012 - sm_links: *id021 -- block_parents: *id014 + - [3, 4] +- block_parents: *id011 sm_links: *id021 -- block_parents: *id015 +- block_parents: *id013 sm_links: *id021 -- block_parents: *id011 - sm_links: &id022 - - [0, 1] - - [0, 2] - - [2, 3] - - [3, 4] -- block_parents: *id012 - sm_links: *id022 - block_parents: *id014 - sm_links: *id022 -- block_parents: *id015 - sm_links: *id022 + sm_links: *id021 diff --git a/tests/generators/fork_choice_generated/sample_invalid_messages.yaml b/tests/generators/fork_choice_generated/sample_invalid_messages.yaml index af7cfd9658..a3f0669faa 100644 --- a/tests/generators/fork_choice_generated/sample_invalid_messages.yaml +++ b/tests/generators/fork_choice_generated/sample_invalid_messages.yaml @@ -1,153 +1,153 @@ - block_parents: [0, 0, 1, 2, 3, 4, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0] - sources: &id001 [0, 0, 2, 3] - targets: &id002 [1, 2, 3, 4] + sm_links: &id001 + - [0, 1] + - [0, 2] + - [2, 3] + - [3, 4] - block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 3, 3, 2, 2, 1, 1, 0, 0] - sources: *id001 - targets: *id002 + sm_links: *id001 - block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 3, 3, 2, 2, 1, 1, 0, 0] - sources: *id001 - targets: *id002 + sm_links: *id001 - block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 3, 2, 2, 1, 1, 0, 0] - sources: *id001 - targets: *id002 -- block_parents: &id004 [0, 0, 0, 0] - sm_links: &id003 + sm_links: *id001 +- block_parents: &id003 [0, 0, 0, 0] + sm_links: &id002 - [0, 1] - [0, 2] - [0, 3] -- block_parents: &id005 [0, 0, 1, 0] - sm_links: *id003 -- block_parents: &id007 [0, 0, 0, 1] - sm_links: *id003 -- block_parents: &id008 [0, 0, 1, 1] - sm_links: *id003 -- block_parents: *id004 - sm_links: &id006 +- block_parents: &id004 [0, 0, 1, 0] + sm_links: *id002 +- block_parents: &id006 [0, 0, 0, 1] + sm_links: *id002 +- block_parents: &id007 [0, 0, 1, 1] + sm_links: *id002 +- block_parents: *id003 + sm_links: &id005 - [0, 1] - [0, 2] - [1, 3] -- block_parents: *id005 - sm_links: *id006 +- block_parents: *id004 + sm_links: *id005 +- block_parents: *id006 + sm_links: *id005 - block_parents: *id007 - sm_links: *id006 -- block_parents: *id008 - sm_links: *id006 + sm_links: *id005 +- block_parents: *id003 + sm_links: &id008 + - [0, 1] + - [0, 2] + - [2, 3] - block_parents: *id004 + sm_links: *id008 +- block_parents: *id006 + sm_links: *id008 +- block_parents: *id007 + sm_links: *id008 +- block_parents: &id010 [0, 0, 0, 0] sm_links: &id009 - [0, 1] - [0, 2] - - [2, 3] -- block_parents: *id005 + - [0, 3] + - [0, 4] +- block_parents: &id011 [0, 0, 1, 0] sm_links: *id009 -- block_parents: *id007 +- block_parents: &id013 [0, 0, 0, 1] sm_links: *id009 -- block_parents: *id008 +- block_parents: &id014 [0, 0, 1, 1] sm_links: *id009 -- block_parents: &id011 [0, 0, 0, 0] - sm_links: &id010 +- block_parents: *id010 + sm_links: &id012 - [0, 1] - [0, 2] - [0, 3] - - [0, 4] -- block_parents: &id012 [0, 0, 1, 0] - sm_links: *id010 -- block_parents: &id014 [0, 0, 0, 1] - sm_links: *id010 -- block_parents: &id015 [0, 0, 1, 1] - sm_links: *id010 + - [1, 4] - block_parents: *id011 - sm_links: &id013 + sm_links: *id012 +- block_parents: *id013 + sm_links: *id012 +- block_parents: *id014 + sm_links: *id012 +- block_parents: *id010 + sm_links: &id015 - [0, 1] - [0, 2] - [0, 3] - - [1, 4] -- block_parents: *id012 - sm_links: *id013 -- block_parents: *id014 - sm_links: *id013 -- block_parents: *id015 - sm_links: *id013 + - [2, 4] - block_parents: *id011 + sm_links: *id015 +- block_parents: *id013 + sm_links: *id015 +- block_parents: *id014 + sm_links: *id015 +- block_parents: *id010 sm_links: &id016 - [0, 1] - [0, 2] - [0, 3] - - [2, 4] -- block_parents: *id012 + - [3, 4] +- block_parents: *id011 sm_links: *id016 -- block_parents: *id014 +- block_parents: *id013 sm_links: *id016 -- block_parents: *id015 +- block_parents: *id014 sm_links: *id016 -- block_parents: *id011 +- block_parents: *id010 sm_links: &id017 - [0, 1] - [0, 2] - - [0, 3] - - [3, 4] -- block_parents: *id012 + - [1, 3] + - [1, 4] +- block_parents: *id011 sm_links: *id017 -- block_parents: *id014 +- block_parents: *id013 sm_links: *id017 -- block_parents: *id015 +- block_parents: *id014 sm_links: *id017 -- block_parents: *id011 +- block_parents: *id010 sm_links: &id018 - [0, 1] - [0, 2] - [1, 3] - - [1, 4] -- block_parents: *id012 + - [2, 4] +- block_parents: *id011 sm_links: *id018 -- block_parents: *id014 +- block_parents: *id013 sm_links: *id018 -- block_parents: *id015 +- block_parents: *id014 sm_links: *id018 -- block_parents: *id011 +- block_parents: *id010 sm_links: &id019 - [0, 1] - [0, 2] - [1, 3] - - [2, 4] -- block_parents: *id012 + - [3, 4] +- block_parents: *id011 sm_links: *id019 -- block_parents: *id014 +- block_parents: *id013 sm_links: *id019 -- block_parents: *id015 +- block_parents: *id014 sm_links: *id019 -- block_parents: *id011 +- block_parents: *id010 sm_links: &id020 - [0, 1] - [0, 2] - - [1, 3] - - [3, 4] -- block_parents: *id012 + - [2, 3] + - [2, 4] +- block_parents: *id011 sm_links: *id020 -- block_parents: *id014 +- block_parents: *id013 sm_links: *id020 -- block_parents: *id015 +- block_parents: *id014 sm_links: *id020 -- block_parents: *id011 +- block_parents: *id010 sm_links: &id021 - [0, 1] - [0, 2] - [2, 3] - - [2, 4] -- block_parents: *id012 - sm_links: *id021 -- block_parents: *id014 + - [3, 4] +- block_parents: *id011 sm_links: *id021 -- block_parents: *id015 +- block_parents: *id013 sm_links: *id021 -- block_parents: *id011 - sm_links: &id022 - - [0, 1] - - [0, 2] - - [2, 3] - - [3, 4] -- block_parents: *id012 - sm_links: *id022 - block_parents: *id014 - sm_links: *id022 -- block_parents: *id015 - sm_links: *id022 + sm_links: *id021 From db5b03fe8885aeb9e584ac5c39f6525b9e2aaf81 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Fri, 12 Apr 2024 00:52:32 +0400 Subject: [PATCH 044/111] renamed GENERATOR_NAME --- .../fork_choice_generated/filter_block_tree_generator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/generators/fork_choice_generated/filter_block_tree_generator.py b/tests/generators/fork_choice_generated/filter_block_tree_generator.py index db0cf26dd0..8014a90afb 100644 --- a/tests/generators/fork_choice_generated/filter_block_tree_generator.py +++ b/tests/generators/fork_choice_generated/filter_block_tree_generator.py @@ -14,7 +14,7 @@ BLS_ACTIVE = False -GENERATOR_NAME = 'filter_block_tree' +GENERATOR_NAME = 'fork_choice_generated' forks = [ALTAIR] From d4b33cbde0a4eff87d5b6bf54b1bade37838d386 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Fri, 12 Apr 2024 00:53:14 +0400 Subject: [PATCH 045/111] fixed solution constuction (`sm_links` instead of `sources/targets`) --- .../fork_choice_generated/generate_test_instances.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/generators/fork_choice_generated/generate_test_instances.py b/tests/generators/fork_choice_generated/generate_test_instances.py index 2819a1dd08..931f4b4501 100644 --- a/tests/generators/fork_choice_generated/generate_test_instances.py +++ b/tests/generators/fork_choice_generated/generate_test_instances.py @@ -124,7 +124,7 @@ def generate_block_cover(params): 'out_path': 'attester_slashings.yaml', 'models': ['sm_link', 'block_tree'], 'params': [ - ([{'sources': [0, 0, 2, 3], 'targets': [1, 2, 3, 4]}], {'number_of_blocks': 16, 'max_children': 3, 'number_of_solutions': 4}), + ([{'sm_links': [[0, 1], [0, 2], [2, 3], [3, 4]]}], {'number_of_blocks': 16, 'max_children': 3, 'number_of_solutions': 4}), ({'anchor_epoch': 0, 'number_of_epochs': 4, 'number_of_links': 3}, {'number_of_blocks': 4, 'max_children': 3, 'number_of_solutions': 4}), ({'anchor_epoch': 0, 'number_of_epochs': 5, 'number_of_links': 4}, {'number_of_blocks': 4, 'max_children': 3, 'number_of_solutions': 4}), ] @@ -133,7 +133,7 @@ def generate_block_cover(params): 'out_path': 'invalid_messages.yaml', 'models': ['sm_link', 'block_tree'], 'params': [ - ([{'sources': [0, 0, 2, 3], 'targets': [1, 2, 3, 4]}], {'number_of_blocks': 16, 'max_children': 3, 'number_of_solutions': 4}), + ([{'sm_links': [[0, 1], [0, 2], [2, 3], [3, 4]]}], {'number_of_blocks': 16, 'max_children': 3, 'number_of_solutions': 4}), ({'anchor_epoch': 0, 'number_of_epochs': 4, 'number_of_links': 3}, {'number_of_blocks': 4, 'max_children': 3, 'number_of_solutions': 4}), ({'anchor_epoch': 0, 'number_of_epochs': 5, 'number_of_links': 4}, {'number_of_blocks': 4, 'max_children': 3, 'number_of_solutions': 4}), ] From 84ca4752a661d89d846fb32cc8d087dfd0313e63 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 16 Apr 2024 17:41:12 +0400 Subject: [PATCH 046/111] Added a command line option to pass the test_gen configuration path --- .../fork_choice_generated/test_gen.py | 56 +++++-------------- .../fork_choice_generated/tg_standard.yaml | 8 +++ 2 files changed, 22 insertions(+), 42 deletions(-) create mode 100644 tests/generators/fork_choice_generated/tg_standard.yaml diff --git a/tests/generators/fork_choice_generated/test_gen.py b/tests/generators/fork_choice_generated/test_gen.py index 40d4bbbe24..2257d1f5cc 100644 --- a/tests/generators/fork_choice_generated/test_gen.py +++ b/tests/generators/fork_choice_generated/test_gen.py @@ -1,4 +1,5 @@ from eth2spec.gen_helpers.gen_base import gen_runner +from ruamel.yaml import YAML from filter_block_tree_generator import ( forks as block_cover_forks, @@ -14,41 +15,11 @@ ) -GENERATOR_NAME = 'fork_choice_generated' +yaml = YAML(typ='safe') + +GENERATOR_NAME = 'fork_choice_generated' -test_gen_config = { - 'block_tree_test': { - 'test_type': 'block_tree', - 'instances': 'block_tree.yaml', - 'seed': 123, - 'nr_variations': 1, - 'nr_mutations': 0, - }, - 'invalid_message_test': { - 'test_type': 'block_tree', - 'with_invalid_messages': True, - 'instances': 'block_tree.yaml', - 'seed': 123, - 'nr_variations': 1, - 'nr_mutations': 1, - }, - 'attester_slashing_test': { - 'test_type': 'block_tree', - 'with_attester_slashings': True, - 'instances': 'block_tree.yaml', - 'seed': 123, - 'nr_variations': 1, - 'nr_mutations': 1, - }, - 'block_cover_test': { - 'test_type': 'block_cover', - 'instances': 'block_cover.yaml', - 'seed': 456, - 'nr_variations': 1, - 'nr_mutations': 0, - } -} if __name__ == "__main__": arg_parser = gen_runner.create_arg_parser(GENERATOR_NAME) @@ -61,18 +32,19 @@ required=False, help='If set provides debug output and enable additional checks for generated chains', ) - # TODO: load test_gen_config from a yaml file - # arg_parser.add_argument( - # '--fc-gen-config', - # dest='fc_gen_config', - # type=str, - # default=None, - # required=False, - # help='Path to a file with test generator configurations' - # ) + arg_parser.add_argument( + '--fc-gen-config', + dest='fc_gen_config', + type=str, + required=True, + help='Path to a file with test generator configurations' + ) args = arg_parser.parse_args() + with open(args.fc_gen_config, 'r') as f: + test_gen_config = yaml.load(f) + for test_name, params in test_gen_config.items(): print(test_name) test_type = params['test_type'] diff --git a/tests/generators/fork_choice_generated/tg_standard.yaml b/tests/generators/fork_choice_generated/tg_standard.yaml new file mode 100644 index 0000000000..6abeeebf7f --- /dev/null +++ b/tests/generators/fork_choice_generated/tg_standard.yaml @@ -0,0 +1,8 @@ +attester_slashing_test: {instances: block_tree.yaml, nr_mutations: 1, nr_variations: 1, + seed: 123, test_type: block_tree, with_attester_slashings: true} +block_cover_test: {instances: block_cover.yaml, nr_mutations: 0, nr_variations: 1, + seed: 456, test_type: block_cover} +block_tree_test: {instances: block_tree.yaml, nr_mutations: 0, nr_variations: 1, seed: 123, + test_type: block_tree} +invalid_message_test: {instances: block_tree.yaml, nr_mutations: 1, nr_variations: 1, + seed: 123, test_type: block_tree, with_invalid_messages: true} From 991d026018da9d5d2db0155e4a668e77981f8d24 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Mon, 22 Apr 2024 17:48:20 +0600 Subject: [PATCH 047/111] Ensure voting source matches the attestation source --- .../phase0/fork_choice/test_sm_links_tree_model.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py index bb68fe759a..a0a6950693 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py @@ -209,7 +209,15 @@ def _compute_validator_partitions(spec, branch_tips, current_links, current_epoc def _get_eligible_attestations(spec, state, attestations) -> []: - return [a for a in attestations if state.slot <= a.data.slot + spec.SLOTS_PER_EPOCH] + def _get_voting_source(target: spec.Checkpoint) -> spec.Checkpoint: + if target.epoch == spec.get_current_epoch(state): + return state.current_justified_checkpoint + else: + return state.previous_justified_checkpoint + + return [a for a in attestations if + state.slot <= a.data.slot + spec.SLOTS_PER_EPOCH + and a.data.source == _get_voting_source(a.data.target)] def _produce_block(spec, state, attestations, attester_slashings=[]): @@ -882,6 +890,7 @@ def test_sm_links_tree_model(spec, # on_block for blocks from the current slot for signed_block_message in (b for b in signed_block_messages if b.payload.message.slot == slot): yield from add_block(spec, store, signed_block_message.payload, test_steps, signed_block_message.valid) + block_root = signed_block_message.payload.message.hash_tree_root() if signed_block_message.valid: assert store.blocks[block_root] == signed_block_message.payload.message From 40aadb31fd4cd6156ea6f077407bf979b393183a Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Mon, 22 Apr 2024 19:48:17 +0600 Subject: [PATCH 048/111] Remove over constraining assert check --- .../test/phase0/fork_choice/test_sm_links_tree_model.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py index a0a6950693..94f2a3a65e 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py @@ -479,9 +479,6 @@ def _generate_sm_link_tree(spec, genesis_state, sm_links, rnd: random.Random, de :return: Sequence of signed blocks oredered by a slot number. """ assert any(sm_links) - # Cases where source == GENESIS_EPOCH + 1 aren't supported, - # because the protocol can't justify in epoch GENESIS_EPOCH + 1 - assert len([sm_link for sm_link in sm_links if sm_link.source == spec.GENESIS_EPOCH + 1]) == 0 # Find anchor epoch anchor_epoch = min(sm_links, key=lambda l: l.source).source @@ -839,7 +836,7 @@ def test_sm_links_tree_model(spec, # Block tree model attestation_messages = [] attester_slashing_messages = [] - if block_parents is not None: + if block_parents is not None and any(block_parents): block_tree, attestation_messages, attester_slashing_messages = _generate_block_tree( spec, highest_tip, rnd, debug, block_parents, with_attester_slashings, with_invalid_messages) # Merge block_tree and sm_link_tree blocks From c4ca1bcc64db99a3565ab1762a0fdc799aa27419 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Tue, 23 Apr 2024 18:03:08 +0600 Subject: [PATCH 049/111] Ensure that epoch 1 is not supposed to be justified --- .../test/phase0/fork_choice/test_sm_links_tree_model.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py index 94f2a3a65e..ba0b4d0356 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py @@ -1033,6 +1033,13 @@ def test_filter_block_tree_model(spec, state, model_params=None, debug=False, se anchor_epoch = block_epochs[0] + # Ensure that there is no attempt to justify GENESIS_EPOCH + 1 as it is not supported by the protocol + for b in range(1, len(block_epochs)): + if block_epochs[b] == spec.GENESIS_EPOCH + 1: + assert not current_justifications[b], 'Justification of epoch 1 is not supported by the protocol' + if block_epochs[b] == spec.GENESIS_EPOCH + 2: + assert not previous_justifications[b], 'Justification of epoch 1 is not supported by the protocol' + # Ensure that epoch(block) == epoch(parent) + 1 for b in range(1, len(block_epochs)): assert block_epochs[b] == block_epochs[parents[b]] + 1, 'epoch(' + str(b) + ') != epoch(' + str( From 4c9e4029dd55f571cd2f3759d97728764e3790d9 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Wed, 24 Apr 2024 00:27:41 +0400 Subject: [PATCH 050/111] Re-formatted test_gen config --- .../fork_choice_generated/tg_standard.yaml | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/tests/generators/fork_choice_generated/tg_standard.yaml b/tests/generators/fork_choice_generated/tg_standard.yaml index 6abeeebf7f..fc424c3dba 100644 --- a/tests/generators/fork_choice_generated/tg_standard.yaml +++ b/tests/generators/fork_choice_generated/tg_standard.yaml @@ -1,8 +1,26 @@ -attester_slashing_test: {instances: block_tree.yaml, nr_mutations: 1, nr_variations: 1, - seed: 123, test_type: block_tree, with_attester_slashings: true} -block_cover_test: {instances: block_cover.yaml, nr_mutations: 0, nr_variations: 1, - seed: 456, test_type: block_cover} -block_tree_test: {instances: block_tree.yaml, nr_mutations: 0, nr_variations: 1, seed: 123, - test_type: block_tree} -invalid_message_test: {instances: block_tree.yaml, nr_mutations: 1, nr_variations: 1, - seed: 123, test_type: block_tree, with_invalid_messages: true} +block_tree_test: + test_type: block_tree + instances: block_tree.yaml + seed: 123 + nr_variations: 1 + nr_mutations: 1 +attester_slashing_test: + test_type: block_tree + instances: block_tree.yaml + seed: 123 + nr_variations: 1 + nr_mutations: 1 + with_attester_slashings: true +invalid_message_test: + test_type: block_tree + instances: block_tree.yaml + seed: 123 + nr_variations: 1 + nr_mutations: 1 + with_invalid_messages: true +block_cover_test: + test_type: block_cover + instances: block_cover.yaml + seed: 456 + nr_variations: 1 + nr_mutations: 1 From c6a14966bfa6ee505c815cd7c521b89e9f437bb3 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Wed, 24 Apr 2024 00:28:55 +0400 Subject: [PATCH 051/111] Refactoring of test generator routines: streamline code organization --- .../filter_block_tree_generator.py | 228 ---------- .../instance_generator.py | 410 ++++++++++++++++++ .../generators/fork_choice_generated/main.py | 236 ---------- .../fork_choice_generated/test_gen.py | 34 +- 4 files changed, 425 insertions(+), 483 deletions(-) delete mode 100644 tests/generators/fork_choice_generated/filter_block_tree_generator.py create mode 100644 tests/generators/fork_choice_generated/instance_generator.py delete mode 100644 tests/generators/fork_choice_generated/main.py diff --git a/tests/generators/fork_choice_generated/filter_block_tree_generator.py b/tests/generators/fork_choice_generated/filter_block_tree_generator.py deleted file mode 100644 index 8014a90afb..0000000000 --- a/tests/generators/fork_choice_generated/filter_block_tree_generator.py +++ /dev/null @@ -1,228 +0,0 @@ -from eth2spec.test.helpers.constants import ALTAIR -from eth2spec.gen_helpers.gen_base import gen_runner -from eth2spec.test.helpers.constants import MINIMAL -from eth2spec.test.helpers.specs import spec_targets -from eth2spec.gen_helpers.gen_base.gen_typing import TestCase, TestProvider -from typing import Iterable -from importlib import import_module -from eth2spec.utils import bls -from eth2spec.test.helpers.typing import SpecForkName, PresetBaseName -from minizinc import Instance, Model, Solver -from mutation_operators import MutatorsGenerator -import random -from ruamel.yaml import YAML - - -BLS_ACTIVE = False -GENERATOR_NAME = 'fork_choice_generated' - - -forks = [ALTAIR] -presets = [MINIMAL] - - -def _load_model_solutions(instance_path: str): - yaml = YAML(typ='safe') - solutions = yaml.load(open(instance_path, 'r')) - print('solutions', solutions) - return solutions - - -def _import_test_fn(): - src = import_module('eth2spec.test.phase0.fork_choice.test_sm_links_tree_model') - print("generating test vectors from tests source: %s" % src.__name__) - return getattr(src, 'test_filter_block_tree_model') - - -def _find_model_solutions(anchor_epoch: int, - store_justified_epoch_equal_zero: bool, - block_voting_source_epoch_equal_store_justified_epoch: bool, - block_voting_source_epoch_plus_two_greater_or_equal_current_epoch: bool, - block_is_leaf: bool, - nr_solutions: int=5) -> []: - block_cover3 = Model('./model/minizinc/Block_cover3.mzn') - solver = Solver.lookup("gecode") - instance = Instance(solver, block_cover3) - instance['AE'] = anchor_epoch - instance['store_je_eq_zero'] = store_justified_epoch_equal_zero - instance['block_vse_eq_store_je'] = block_voting_source_epoch_equal_store_justified_epoch - instance['block_vse_plus_two_ge_curr_e'] = block_voting_source_epoch_plus_two_greater_or_equal_current_epoch - instance['block_is_leaf'] = block_is_leaf - - result = instance.solve(nr_solutions=nr_solutions) - - output = [] - for s in result.solution: - max_block = s.max_block - output.append({'block_epochs': s.es[:max_block + 1], - 'parents': s.parents[:max_block + 1], - 'previous_justifications': s.prevs[:max_block + 1], - 'current_justifications': s.currs[:max_block + 1], - 'current_epoch': s.curr_e, - 'store_justified_epoch': s.store_je, - 'target_block': s.target_block, - 'predicates': { - 'store_je_eq_zero': store_justified_epoch_equal_zero, - 'block_vse_eq_store_je': block_voting_source_epoch_equal_store_justified_epoch, - 'block_vse_plus_two_ge_curr_e': block_voting_source_epoch_plus_two_greater_or_equal_current_epoch, - 'block_is_leaf': block_is_leaf - }}) - - return output - - -def _generate_model_solutions(anchor_epoch: int, nr_solutions: int=5): - solutions = [] - - for store_je_eq_zero in [True, False]: - for block_vse_eq_store_je in [True, False]: - for block_vse_plus_two_ge_curr_e in [True, False]: - for block_is_leaf in [True, False]: - if store_je_eq_zero and not block_vse_eq_store_je: - continue - results = _find_model_solutions(anchor_epoch=0 if store_je_eq_zero else anchor_epoch, - store_justified_epoch_equal_zero=store_je_eq_zero, - block_voting_source_epoch_equal_store_justified_epoch=block_vse_eq_store_je, - block_voting_source_epoch_plus_two_greater_or_equal_current_epoch=block_vse_plus_two_ge_curr_e, - block_is_leaf=block_is_leaf, - nr_solutions=nr_solutions) - print('\n\n') - print(['store_je_eq_zero=' + str(store_je_eq_zero), - 'block_vse_eq_store_je=' + str(block_vse_eq_store_je), - 'block_vse_plus_two_ge_curr_e=' + str(block_vse_plus_two_ge_curr_e), - 'block_is_leaf=' + str(block_is_leaf)]) - for r in results: - print(r) - - solutions.extend(results) - - return solutions - - -def _create_providers(test_name: str, /, - forks: Iterable[SpecForkName], - presets: Iterable[PresetBaseName], - debug: bool, - initial_seed: int, - solutions, - number_of_variations: int, - number_of_mutations: int) -> Iterable[TestProvider]: - def prepare_fn() -> None: - bls.use_milagro() - return - - def make_cases_fn() -> Iterable[TestCase]: - _test_fn = _import_test_fn() - def test_fn(phase: str, preset: str, seed: int, solution): - return _test_fn(generator_mode=True, - phase=phase, - preset=preset, - bls_active=BLS_ACTIVE, - debug=debug, - seed=seed, - model_params=solution) - - seeds = [initial_seed] - if number_of_variations > 1: - rnd = random.Random(initial_seed) - seeds = [rnd.randint(1, 10000) for _ in range(number_of_variations)] - seeds[0] = initial_seed - - for i, solution in enumerate(solutions): - for seed in seeds: - for fork_name in forks: - for preset_name in presets: - spec = spec_targets[preset_name][fork_name] - mutation_generator = MutatorsGenerator( - spec, seed, number_of_mutations, - lambda: test_fn(fork_name, preset_name, seed, solution), - debug=debug) - for j in range(1 + number_of_mutations): - yield TestCase(fork_name=fork_name, - preset_name=preset_name, - runner_name=GENERATOR_NAME, - handler_name=test_name, - suite_name='fork_choice', - case_name=test_name + '_' + str(i) + '_' + str(seed) + '_' + str(j), - case_fn=mutation_generator.next_test_case) - - yield TestProvider(prepare=prepare_fn, make_cases=make_cases_fn) - - -if __name__ == "__main__": - arg_parser = gen_runner.create_arg_parser(GENERATOR_NAME) - - arg_parser.add_argument( - '--fc-gen-debug', - dest='fc_gen_debug', - action='store_true', - default=False, - required=False, - help='If set provides debug output and enable additional checks for generated chains', - ) - arg_parser.add_argument( - '--fc-gen-seed', - dest='fc_gen_seed', - default=1, - type=int, - required=False, - help='Provides randomizer with initial seed' - ) - arg_parser.add_argument( - '--fc-gen-variations', - dest='fc_gen_variations', - default=1, - type=int, - required=False, - help='Number of random variations per each solution' - ) - arg_parser.add_argument( - '--fc-gen-anchor-epoch', - dest='fc_gen_anchor_epoch', - default=0, - type=int, - required=False, - help='Anchor epoch' - ) - arg_parser.add_argument( - '--fc-gen-nr-solutions', - dest='fc_gen_nr_solutions', - default=5, - type=int, - required=False, - help='Number of solutions per MiniZinc query' - ) - arg_parser.add_argument( - '--fc-gen-mutations', - dest='fc_gen_mutations', - default=0, - type=int, - required=False, - help='Number of mutations per base test case' - ) - arg_parser.add_argument( - '--fc-gen-instances-path', - dest='fc_gen_instances_path', - default=None, - type=str, - required=False, - help='Path to a file with pre-generated instances' - ) - - args = arg_parser.parse_args() - - if args.fc_gen_instances_path is not None: - solutions = _load_model_solutions(args.fc_gen_instances_path) - else: - solutions = _generate_model_solutions(args.fc_gen_anchor_epoch, args.fc_gen_nr_solutions) - - gen_runner.run_generator(GENERATOR_NAME, - _create_providers('filter_block_tree_model', - forks=forks, - presets=presets, - debug=args.fc_gen_debug, - initial_seed=args.fc_gen_seed, - solutions=solutions, - number_of_variations=args.fc_gen_variations, - number_of_mutations=args.fc_gen_mutations), - arg_parser) diff --git a/tests/generators/fork_choice_generated/instance_generator.py b/tests/generators/fork_choice_generated/instance_generator.py new file mode 100644 index 0000000000..628fcc4fcd --- /dev/null +++ b/tests/generators/fork_choice_generated/instance_generator.py @@ -0,0 +1,410 @@ +from eth2spec.test.helpers.constants import ALTAIR +from eth2spec.gen_helpers.gen_base import gen_runner +from eth2spec.test.helpers.constants import MINIMAL +from eth2spec.test.helpers.specs import spec_targets +from eth2spec.gen_helpers.gen_base.gen_typing import TestCase, TestProvider +from itertools import product +from toolz.dicttoolz import merge +from typing import Iterable +from importlib import import_module +from eth2spec.utils import bls +from eth2spec.test.helpers.typing import SpecForkName, PresetBaseName +from minizinc import Instance, Model, Solver +from ruamel.yaml import YAML +from mutation_operators import mk_mutations, MutatorsGenerator +import random + + +BLS_ACTIVE = False +GENERATOR_NAME = 'fork_choice_generated' + + +forks = [ALTAIR] +presets = [MINIMAL] + + +def _import_block_tree_test_fn(): + src = import_module('eth2spec.test.phase0.fork_choice.test_sm_links_tree_model') + print("generating test vectors from tests source: %s" % src.__name__) + return getattr(src, 'test_sm_links_tree_model') + + +def _find_sm_link_solutions(anchor_epoch: int, + number_of_epochs: int, + number_of_links: int) -> Iterable[Iterable[tuple]]: + # Dependencies: + # 1. Install minizinc binary + # https://www.minizinc.org/doc-2.5.5/en/installation_detailed_linux.html + # 2. Install minizinc python lib + # pip install minizinc + # 3. Install and confifure gecode solver: + # https://www.minizinc.org/doc-2.5.5/en/installation_detailed_linux.html#gecode + sm_links = Model('./model/minizinc/SM_links.mzn') + solver = Solver.lookup("gecode") + instance = Instance(solver, sm_links) + instance['AE'] = anchor_epoch # anchor epoch + instance['NE'] = number_of_epochs # number of epochs, starting from AE + instance['NL'] = number_of_links # number of super-majority links + + solutions = instance.solve(all_solutions=True) + for i in range(len(solutions)): + yield {'sm_links': list(zip(solutions[i, 'sources'], solutions[i, 'targets']))} + + +def _find_block_tree_solutions(number_of_blocks: int, + max_children: int, + number_of_solutions: int) -> Iterable[dict]: + model = Model('./model/minizinc/Block_tree.mzn') + solver = Solver.lookup("gecode") + instance = Instance(solver, model) + instance['NB'] = number_of_blocks + instance['MC'] = max_children + + solutions = instance.solve(nr_solutions=number_of_solutions) + return [{'block_parents': s.parent} for s in solutions] + + +def _load_block_tree_instances(instance_path: str) -> Iterable[dict]: + yaml = YAML(typ='safe') + solutions = yaml.load(open(instance_path, 'r')) + return solutions + + +def _create_block_tree_providers(test_name: str, /, + forks: Iterable[SpecForkName], + presets: Iterable[PresetBaseName], + debug: bool, + initial_seed: int, + solutions: Iterable, + number_of_variations: int, + number_of_mutations: int, + with_attester_slashings: bool, + with_invalid_messages: bool) -> Iterable[TestProvider]: + def prepare_fn() -> None: + bls.use_milagro() + return + + def make_cases_fn() -> Iterable[TestCase]: + _test_fn = _import_block_tree_test_fn() + + def test_fn(phase: str, preset: str, seed: int, sm_links, block_parents): + return _test_fn(generator_mode=True, + phase=phase, + preset=preset, + bls_active=BLS_ACTIVE, + debug=debug, + seed=seed, + sm_links=sm_links, + block_parents=block_parents, + with_attester_slashings=with_attester_slashings, + with_invalid_messages=with_invalid_messages) + + seeds = [initial_seed] + if number_of_variations > 1: + rnd = random.Random(initial_seed) + seeds = [rnd.randint(1, 10000) for _ in range(number_of_variations)] + seeds[0] = initial_seed + + for i, solution in enumerate(solutions): + for seed in seeds: + for fork_name in forks: + for preset_name in presets: + spec = spec_targets[preset_name][fork_name] + mutation_generator = MutatorsGenerator( + spec, seed, number_of_mutations, + lambda: test_fn(fork_name, preset_name, seed, + sm_links=solution['sm_links'], block_parents=solution['block_parents']), + debug=debug) + for j in range(1 + number_of_mutations): + yield TestCase(fork_name=fork_name, + preset_name=preset_name, + runner_name=GENERATOR_NAME, + handler_name=test_name, + suite_name='fork_choice', + case_name=test_name + '_' + str(i) + '_' + str(seed) + '_' + str(j), + case_fn=mutation_generator.next_test_case) + + yield TestProvider(prepare=prepare_fn, make_cases=make_cases_fn) + + +def _import_block_cover_test_fn(): + src = import_module('eth2spec.test.phase0.fork_choice.test_sm_links_tree_model') + print("generating test vectors from tests source: %s" % src.__name__) + return getattr(src, 'test_filter_block_tree_model') + + +def _find_block_cover_model_solutions(anchor_epoch: int, + store_justified_epoch_equal_zero: bool, + block_voting_source_epoch_equal_store_justified_epoch: bool, + block_voting_source_epoch_plus_two_greater_or_equal_current_epoch: bool, + block_is_leaf: bool, + nr_solutions: int=5) -> []: + block_cover3 = Model('./model/minizinc/Block_cover3.mzn') + solver = Solver.lookup("gecode") + instance = Instance(solver, block_cover3) + instance['AE'] = anchor_epoch + instance['store_je_eq_zero'] = store_justified_epoch_equal_zero + instance['block_vse_eq_store_je'] = block_voting_source_epoch_equal_store_justified_epoch + instance['block_vse_plus_two_ge_curr_e'] = block_voting_source_epoch_plus_two_greater_or_equal_current_epoch + instance['block_is_leaf'] = block_is_leaf + + result = instance.solve(nr_solutions=nr_solutions) + + output = [] + for s in result.solution: + max_block = s.max_block + output.append({'block_epochs': s.es[:max_block + 1], + 'parents': s.parents[:max_block + 1], + 'previous_justifications': s.prevs[:max_block + 1], + 'current_justifications': s.currs[:max_block + 1], + 'current_epoch': s.curr_e, + 'store_justified_epoch': s.store_je, + 'target_block': s.target_block, + 'predicates': { + 'store_je_eq_zero': store_justified_epoch_equal_zero, + 'block_vse_eq_store_je': block_voting_source_epoch_equal_store_justified_epoch, + 'block_vse_plus_two_ge_curr_e': block_voting_source_epoch_plus_two_greater_or_equal_current_epoch, + 'block_is_leaf': block_is_leaf + }}) + + return output + + +def _generate_block_cover_model_solutions(anchor_epoch: int, nr_solutions: int=5, debug=False): + solutions = [] + + for store_je_eq_zero in [True, False]: + for block_vse_eq_store_je in [True, False]: + for block_vse_plus_two_ge_curr_e in [True, False]: + for block_is_leaf in [True, False]: + if store_je_eq_zero and not block_vse_eq_store_je: + continue + if anchor_epoch == 0 and not store_je_eq_zero: + continue + results = _find_block_cover_model_solutions( + anchor_epoch=0 if store_je_eq_zero else anchor_epoch, + store_justified_epoch_equal_zero=store_je_eq_zero, + block_voting_source_epoch_equal_store_justified_epoch=block_vse_eq_store_je, + block_voting_source_epoch_plus_two_greater_or_equal_current_epoch=block_vse_plus_two_ge_curr_e, + block_is_leaf=block_is_leaf, + nr_solutions=nr_solutions) + if debug: + print('\n\n') + print(['store_je_eq_zero=' + str(store_je_eq_zero), + 'block_vse_eq_store_je=' + str(block_vse_eq_store_je), + 'block_vse_plus_two_ge_curr_e=' + str(block_vse_plus_two_ge_curr_e), + 'block_is_leaf=' + str(block_is_leaf)]) + for r in results: + print(r) + + solutions.extend(results) + + return solutions + + +def _load_block_cover_instances(instance_path: str): + yaml = YAML(typ='safe') + solutions = yaml.load(open(instance_path, 'r')) + return solutions + + +def _create_block_cover_providers(test_name: str, /, + forks: Iterable[SpecForkName], + presets: Iterable[PresetBaseName], + debug: bool, + initial_seed: int, + solutions, + number_of_variations: int, + number_of_mutations: int) -> Iterable[TestProvider]: + def prepare_fn() -> None: + bls.use_milagro() + return + + def make_cases_fn() -> Iterable[TestCase]: + _test_fn = _import_block_cover_test_fn() + def test_fn(phase: str, preset: str, seed: int, solution): + return _test_fn(generator_mode=True, + phase=phase, + preset=preset, + bls_active=BLS_ACTIVE, + debug=debug, + seed=seed, + model_params=solution) + + seeds = [initial_seed] + if number_of_variations > 1: + rnd = random.Random(initial_seed) + seeds = [rnd.randint(1, 10000) for _ in range(number_of_variations)] + seeds[0] = initial_seed + + for i, solution in enumerate(solutions): + for seed in seeds: + for fork_name in forks: + for preset_name in presets: + spec = spec_targets[preset_name][fork_name] + mutation_generator = MutatorsGenerator( + spec, seed, number_of_mutations, + lambda: test_fn(fork_name, preset_name, seed, solution), + debug=debug) + for j in range(1 + number_of_mutations): + yield TestCase(fork_name=fork_name, + preset_name=preset_name, + runner_name=GENERATOR_NAME, + handler_name=test_name, + suite_name='fork_choice', + case_name=test_name + '_' + str(i) + '_' + str(seed) + '_' + str(j), + case_fn=mutation_generator.next_test_case) + + yield TestProvider(prepare=prepare_fn, make_cases=make_cases_fn) + + +if __name__ == "__main__": + arg_parser = gen_runner.create_arg_parser(GENERATOR_NAME) + + arg_parser.add_argument( + '--fc-gen-debug', + dest='fc_gen_debug', + action='store_true', + default=False, + required=False, + help='If set provides debug output and enable additional checks for generated chains', + ) + arg_parser.add_argument( + '--fc-gen-test-kind', + dest='fc_gen_test_kind', + type=str, + required=True, + help='Test kind: block_tree or block_cover' + ) + arg_parser.add_argument( + '--fc-gen-seed', + dest='fc_gen_seed', + default=1, + type=int, + required=False, + help='Provides randomizer with initial seed' + ) + arg_parser.add_argument( + '--fc-gen-variations', + dest='fc_gen_variations', + default=1, + type=int, + required=False, + help='Number of random variations per each solution' + ) + arg_parser.add_argument( + '--fc-gen-anchor-epoch', + dest='fc_gen_anchor_epoch', + default=0, + type=int, + required=False, + help='Anchor epoch' + ) + arg_parser.add_argument( + '--fc-gen-epochs', + dest='fc_gen_epochs', + default=2, + type=int, + required=False, + help='Number of epochs beyond the anchor epoch' + ) + arg_parser.add_argument( + '--fc-gen-links', + dest='fc_gen_links', + default=1, + type=int, + required=False, + help='Number of super majority links per solution' + ) + arg_parser.add_argument( + '--fc-gen-mutations', + dest='fc_gen_mutations', + default=0, + type=int, + required=False, + help='Number of mutations per base test case' + ) + arg_parser.add_argument( + '--fc-gen-attester-slashings', + dest='fc_gen_attester_slashings', + default=False, + type=bool, + required=False, + help='Pass with_attester_slashings flag' + ) + arg_parser.add_argument( + '--fc-gen-invalid-messages', + dest='fc_gen_invalid_messages', + default=False, + type=bool, + required=False, + help='Pass with_invalid_messages flag' + ) + arg_parser.add_argument( + '--fc-gen-instances-path', + dest='fc_gen_instances_path', + default=None, + type=str, + required=False, + help='Path to a file with pre-generated instances' + ) + arg_parser.add_argument( + '--fc-gen-nr-solutions', + dest='fc_gen_nr_solutions', + default=5, + type=int, + required=False, + help='Number of solutions per MiniZinc query' + ) + + args = arg_parser.parse_args() + + if args.fc_gen_test_kind == 'block_tree': + if args.fc_gen_instances_path is not None: + solutions = _load_block_tree_instances(args.fc_gen_instances_path) + else: + sm_link_solutions = _find_sm_link_solutions(args.fc_gen_anchor_epoch, args.fc_gen_epochs, args.fc_gen_links) + block_tree_solutions = _find_block_tree_solutions(16, 3, 3) + solutions = [merge(*sols) for sols in product(sm_link_solutions, block_tree_solutions)] + + if not args.fc_gen_attester_slashings and not args.fc_gen_invalid_messages: + test_name = 'block_tree' + elif args.fc_gen_attester_slashings and not args.fc_gen_invalid_messages: + test_name = 'attester_slashings' + elif args.fc_gen_invalid_messages and not args.fc_gen_attester_slashings: + test_name = 'invalid_messages' + else: + test_name = 'attester_slashings_and_invalid_messages' + + gen_runner.run_generator(GENERATOR_NAME, + _create_block_tree_providers(test_name, + forks=forks, + presets=presets, + debug=args.fc_gen_debug, + initial_seed=args.fc_gen_seed, + solutions=solutions, + number_of_variations=args.fc_gen_variations, + number_of_mutations=args.fc_gen_mutations, + with_attester_slashings=args.fc_gen_attester_slashings, + with_invalid_messages=args.fc_gen_invalid_messages), + arg_parser) + elif args.fc_gen_test_kind == 'block_cover': + if args.fc_gen_instances_path is not None: + solutions = _load_block_cover_instances(args.fc_gen_instances_path) + else: + solutions = _generate_block_cover_model_solutions(args.fc_gen_anchor_epoch, args.fc_gen_nr_solutions, args.fc_gen_debug) + + test_name = 'block_cover' + gen_runner.run_generator(GENERATOR_NAME, + _create_block_cover_providers( + test_name, + forks=forks, + presets=presets, + debug=args.fc_gen_debug, + initial_seed=args.fc_gen_seed, + solutions=solutions, + number_of_variations=args.fc_gen_variations, + number_of_mutations=args.fc_gen_mutations), + arg_parser) + else: + raise ValueError(f'Unsupported test kind: {args.fc_gen_test_kind}') diff --git a/tests/generators/fork_choice_generated/main.py b/tests/generators/fork_choice_generated/main.py deleted file mode 100644 index 7c41ec99b6..0000000000 --- a/tests/generators/fork_choice_generated/main.py +++ /dev/null @@ -1,236 +0,0 @@ -from eth2spec.test.helpers.constants import ALTAIR -from eth2spec.gen_helpers.gen_base import gen_runner -from eth2spec.test.helpers.constants import MINIMAL -from eth2spec.test.helpers.specs import spec_targets -from eth2spec.gen_helpers.gen_base.gen_typing import TestCase, TestProvider -from itertools import product -from toolz.dicttoolz import merge -from typing import Iterable -from importlib import import_module -from eth2spec.utils import bls -from eth2spec.test.helpers.typing import SpecForkName, PresetBaseName -from minizinc import Instance, Model, Solver -from ruamel.yaml import YAML -from mutation_operators import mk_mutations, MutatorsGenerator -import random - - -yaml = YAML(typ='safe') - - -BLS_ACTIVE = False -GENERATOR_NAME = 'fork_choice_generated' - - -forks = [ALTAIR] -presets = [MINIMAL] - - -def _import_test_fn(): - src = import_module('eth2spec.test.phase0.fork_choice.test_sm_links_tree_model') - print("generating test vectors from tests source: %s" % src.__name__) - return getattr(src, 'test_sm_links_tree_model') - - -def _find_sm_link_solutions(anchor_epoch: int, - number_of_epochs: int, - number_of_links: int) -> Iterable[Iterable[tuple]]: - # Dependencies: - # 1. Install minizinc binary - # https://www.minizinc.org/doc-2.5.5/en/installation_detailed_linux.html - # 2. Install minizinc python lib - # pip install minizinc - # 3. Install and confifure gecode solver: - # https://www.minizinc.org/doc-2.5.5/en/installation_detailed_linux.html#gecode - sm_links = Model('./model/minizinc/SM_links.mzn') - solver = Solver.lookup("gecode") - instance = Instance(solver, sm_links) - instance['AE'] = anchor_epoch # anchor epoch - instance['NE'] = number_of_epochs # number of epochs, starting from AE - instance['NL'] = number_of_links # number of super-majority links - - solutions = instance.solve(all_solutions=True) - for i in range(len(solutions)): - yield {'sm_links': list(zip(solutions[i, 'sources'], solutions[i, 'targets']))} - - -def _find_block_tree_solutions(number_of_blocks: int, - max_children: int, - number_of_solutions: int) -> Iterable[dict]: - model = Model('./model/minizinc/Block_tree.mzn') - solver = Solver.lookup("gecode") - instance = Instance(solver, model) - instance['NB'] = number_of_blocks - instance['MC'] = max_children - - solutions = instance.solve(nr_solutions=number_of_solutions) - return [{'block_parents': s.parent} for s in solutions] - - -def _load_block_tree_instances(instance_path: str) -> Iterable[dict]: - solutions = yaml.load(open(instance_path, 'r')) - return solutions - - -def _create_providers(test_name: str, /, - forks: Iterable[SpecForkName], - presets: Iterable[PresetBaseName], - debug: bool, - initial_seed: int, - solutions: Iterable, - number_of_variations: int, - number_of_mutations: int, - with_attester_slashings: bool, - with_invalid_messages: bool) -> Iterable[TestProvider]: - def prepare_fn() -> None: - bls.use_milagro() - return - - def make_cases_fn() -> Iterable[TestCase]: - _test_fn = _import_test_fn() - - def test_fn(phase: str, preset: str, seed: int, sm_links, block_parents): - return _test_fn(generator_mode=True, - phase=phase, - preset=preset, - bls_active=BLS_ACTIVE, - debug=debug, - seed=seed, - sm_links=sm_links, - block_parents=block_parents, - with_attester_slashings=with_attester_slashings, - with_invalid_messages=with_invalid_messages) - - seeds = [initial_seed] - if number_of_variations > 1: - rnd = random.Random(initial_seed) - seeds = [rnd.randint(1, 10000) for _ in range(number_of_variations)] - seeds[0] = initial_seed - - for i, solution in enumerate(solutions): - for seed in seeds: - for fork_name in forks: - for preset_name in presets: - spec = spec_targets[preset_name][fork_name] - mutation_generator = MutatorsGenerator( - spec, seed, number_of_mutations, - lambda: test_fn(fork_name, preset_name, seed, - sm_links=solution['sm_links'], block_parents=solution['block_parents']), - debug=debug) - for j in range(1 + number_of_mutations): - yield TestCase(fork_name=fork_name, - preset_name=preset_name, - runner_name=GENERATOR_NAME, - handler_name=test_name, - suite_name='fork_choice', - case_name=test_name + '_' + str(i) + '_' + str(seed) + '_' + str(j), - case_fn=mutation_generator.next_test_case) - - yield TestProvider(prepare=prepare_fn, make_cases=make_cases_fn) - - -if __name__ == "__main__": - arg_parser = gen_runner.create_arg_parser(GENERATOR_NAME) - - arg_parser.add_argument( - '--fc-gen-debug', - dest='fc_gen_debug', - action='store_true', - default=False, - required=False, - help='If set provides debug output and enable additional checks for generated chains', - ) - arg_parser.add_argument( - '--fc-gen-seed', - dest='fc_gen_seed', - default=1, - type=int, - required=False, - help='Provides randomizer with initial seed' - ) - arg_parser.add_argument( - '--fc-gen-variations', - dest='fc_gen_variations', - default=1, - type=int, - required=False, - help='Number of random variations per each solution' - ) - arg_parser.add_argument( - '--fc-gen-anchor-epoch', - dest='fc_gen_anchor_epoch', - default=0, - type=int, - required=False, - help='Anchor epoch' - ) - arg_parser.add_argument( - '--fc-gen-epochs', - dest='fc_gen_epochs', - default=2, - type=int, - required=False, - help='Number of epochs beyond the anchor epoch' - ) - arg_parser.add_argument( - '--fc-gen-links', - dest='fc_gen_links', - default=1, - type=int, - required=False, - help='Number of super majority links per solution' - ) - arg_parser.add_argument( - '--fc-gen-mutations', - dest='fc_gen_mutations', - default=0, - type=int, - required=False, - help='Number of mutations per base test case' - ) - arg_parser.add_argument( - '--fc-gen-attester-slashings', - dest='fc_gen_attester_slashings', - default=False, - type=bool, - required=False, - help='Pass with_attester_slashings flag' - ) - arg_parser.add_argument( - '--fc-gen-invalid-messages', - dest='fc_gen_invalid_messages', - default=False, - type=bool, - required=False, - help='Pass with_invalid_messages flag' - ) - arg_parser.add_argument( - '--fc-gen-instances-path', - dest='fc_gen_instances_path', - default=None, - type=str, - required=False, - help='Path to a file with pre-generated instances' - ) - - args = arg_parser.parse_args() - - if args.fc_gen_instances_path is not None: - solutions = _load_block_tree_instances(args.fc_gen_instances_path) - else: - sm_link_solutions = _find_sm_link_solutions(args.fc_gen_anchor_epoch, args.fc_gen_epochs, args.fc_gen_links) - block_tree_solutions = _find_block_tree_solutions(16, 3, 3) - solutions = [merge(*sols) for sols in product(sm_link_solutions, block_tree_solutions)] - - gen_runner.run_generator(GENERATOR_NAME, - _create_providers('sm_links_tree_model', - forks=forks, - presets=presets, - debug=args.fc_gen_debug, - initial_seed=args.fc_gen_seed, - solutions=solutions, - number_of_variations=args.fc_gen_variations, - number_of_mutations=args.fc_gen_mutations, - with_attester_slashings=args.fc_gen_attester_slashings, - with_invalid_messages=args.fc_gen_invalid_messages), - arg_parser) diff --git a/tests/generators/fork_choice_generated/test_gen.py b/tests/generators/fork_choice_generated/test_gen.py index 2257d1f5cc..d74eb38cc1 100644 --- a/tests/generators/fork_choice_generated/test_gen.py +++ b/tests/generators/fork_choice_generated/test_gen.py @@ -1,17 +1,13 @@ from eth2spec.gen_helpers.gen_base import gen_runner from ruamel.yaml import YAML -from filter_block_tree_generator import ( - forks as block_cover_forks, - presets as block_cover_presets, - _load_model_solutions as block_cover_load_solutions, - _create_providers as block_cover_create_providers -) -from main import ( - forks as block_tree_forks, - presets as block_tree_presets, - _load_block_tree_instances as block_tree_load_solutions, - _create_providers as block_tree_create_providers +from instance_generator import ( + forks, + presets, + _load_block_tree_instances, + _create_block_tree_providers, + _load_block_cover_instances, + _create_block_cover_providers ) @@ -56,10 +52,10 @@ with_invalid_messages = params.get('with_invalid_messages', False) if test_type == 'block_tree': - solutions = block_tree_load_solutions(instances_path) - providers = block_tree_create_providers(test_name, - forks=block_tree_forks, - presets=block_tree_presets, + solutions = _load_block_tree_instances(instances_path) + providers = _create_block_tree_providers(test_name, + forks=forks, + presets=presets, debug=args.fc_gen_debug, initial_seed=initial_seed, solutions=solutions, @@ -68,10 +64,10 @@ with_attester_slashings=with_attester_slashings, with_invalid_messages=with_invalid_messages) elif test_type == 'block_cover': - solutions = block_cover_load_solutions(instances_path) - providers = block_cover_create_providers(test_name, - forks=block_cover_forks, - presets=block_cover_presets, + solutions = _load_block_cover_instances(instances_path) + providers = _create_block_cover_providers(test_name, + forks=forks, + presets=presets, debug=args.fc_gen_debug, initial_seed=initial_seed, solutions=solutions, From 6bf265a177d4cff5d7c89ce4a4ac474d8cf17fc2 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 25 Apr 2024 00:25:57 +0700 Subject: [PATCH 052/111] Improve block cover instantiator to accommodate for more blocks --- .../fork_choice/test_sm_links_tree_model.py | 34 ++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py index ba0b4d0356..3771bd4fa7 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py @@ -900,6 +900,23 @@ def test_sm_links_tree_model(spec, yield 'steps', test_steps +def _should_be_justified(parents, block_epochs, current_justifications, previous_justifications, epoch) -> bool: + blocks = [i for i, e in enumerate(block_epochs) if e == epoch] + children = [i for i, p in enumerate(parents) if (p in blocks)] + + # Check if any block justifies the epoch + for b in blocks: + if current_justifications[b]: + return True + + # Check if a child of any block justifies the epoch + for c in children: + if previous_justifications[c]: + return True + + return False + + def _generate_filter_block_tree(spec, genesis_state, block_epochs, parents, previous_justifications, current_justifications, rnd: random.Random, debug) -> ([], []): anchor_epoch = block_epochs[0] @@ -917,9 +934,7 @@ def _generate_filter_block_tree(spec, genesis_state, block_epochs, parents, prev if len(current_blocks) == 0: continue - # There should be enough slots to propose all blocks - assert (spec.SLOTS_PER_EPOCH - JUSTIFYING_SLOT) >= len( - current_blocks), "Unsatisfiable constraints: not enough slots to propose all blocks: " + str(current_blocks) + assert len(current_blocks) <= spec.SLOTS_PER_EPOCH, 'Number of blocks does not fit into an epoch' # Case 2. Blocks are from disjoint subtrees -- not supported yet assert len( @@ -931,7 +946,16 @@ def _generate_filter_block_tree(spec, genesis_state, block_epochs, parents, prev state = ancestor_tip.beacon_state attestations = ancestor_tip.attestations - threshold_slot = spec.compute_start_slot_at_epoch(epoch) + JUSTIFYING_SLOT + if _should_be_justified(parents, block_epochs, current_justifications, previous_justifications, epoch): + common_prefix_len = JUSTIFYING_SLOT + else: + common_prefix_len = min(JUSTIFYING_SLOT, spec.SLOTS_PER_EPOCH - len(current_blocks)) + + threshold_slot = spec.compute_start_slot_at_epoch(epoch) + common_prefix_len + + # There should be enough slots to propose all blocks + assert (spec.SLOTS_PER_EPOCH - common_prefix_len) >= len( + current_blocks), "Unsatisfiable constraints: not enough slots to propose all blocks: " + str(current_blocks) # Build the chain up to but excluding a block that will justify current checkpoint while (state.slot < threshold_slot): @@ -956,7 +980,7 @@ def _generate_filter_block_tree(spec, genesis_state, block_epochs, parents, prev # i.e. block capacity is enough to accommodate attestations to justify previus and current epoch checkpoints # if that needed. Considering that most of attestations were already included into the common chain prefix, # we assume it is possible - empty_slot_count = spec.SLOTS_PER_EPOCH - JUSTIFYING_SLOT - len(current_blocks) + empty_slot_count = spec.SLOTS_PER_EPOCH - common_prefix_len - len(current_blocks) block_distribution = current_blocks.copy() + [-1 for _ in range(0, empty_slot_count)] # Randomly distribute blocks across slots From 0ff437af30360922aec0260df842e5b78c642e32 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 25 Apr 2024 17:06:44 +0400 Subject: [PATCH 053/111] Fix unexpected justifed checkpoint in block cover --- .../test/phase0/fork_choice/test_sm_links_tree_model.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py index 3771bd4fa7..fafc452f92 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py @@ -964,7 +964,13 @@ def _generate_filter_block_tree(spec, genesis_state, block_epochs, parents, prev new_block, state, _, _ = _produce_block(spec, state, []) signed_blocks.append(new_block) else: - new_block, state, attestations, _ = _produce_block(spec, state, attestations) + # Prevent previous epoch from being accidentally justified + # by filtering out previous epoch attestations + curr_epoch_attestations = [a for a in attestations if + epoch == spec.compute_epoch_at_slot(a.data.slot)] + other_attestations = [a for a in attestations if a not in curr_epoch_attestations] + new_block, state, curr_epoch_attestations, _ = _produce_block(spec, state, curr_epoch_attestations) + attestations = other_attestations + curr_epoch_attestations signed_blocks.append(new_block) # Attest From 00abe77fba75a47fca3979ce393391ad9022ad08 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 25 Apr 2024 17:54:05 +0400 Subject: [PATCH 054/111] Make block cover instantiator more flexible --- .../fork_choice/test_sm_links_tree_model.py | 50 +++++++++++-------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py index fafc452f92..9f270d9097 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py @@ -900,17 +900,12 @@ def test_sm_links_tree_model(spec, yield 'steps', test_steps -def _should_be_justified(parents, block_epochs, current_justifications, previous_justifications, epoch) -> bool: - blocks = [i for i, e in enumerate(block_epochs) if e == epoch] - children = [i for i, p in enumerate(parents) if (p in blocks)] - - # Check if any block justifies the epoch - for b in blocks: - if current_justifications[b]: - return True +def _should_justifiy_epoch(parents, block_epochs, current_justifications, previous_justifications, block, epoch) -> bool: + if current_justifications[block]: + return True - # Check if a child of any block justifies the epoch - for c in children: + # Check if any child of the block justifies the epoch + for c in (b for b, p in enumerate(parents) if p == block): if previous_justifications[c]: return True @@ -919,23 +914,34 @@ def _should_be_justified(parents, block_epochs, current_justifications, previous def _generate_filter_block_tree(spec, genesis_state, block_epochs, parents, previous_justifications, current_justifications, rnd: random.Random, debug) -> ([], []): + JUSTIFYING_SLOT = 2 * spec.SLOTS_PER_EPOCH // 3 + 1 + JUSTIFYING_SLOT_COUNT = spec.SLOTS_PER_EPOCH - JUSTIFYING_SLOT + anchor_epoch = block_epochs[0] + # Run constraint checks before starting to generate blocks + for epoch in range(anchor_epoch + 1, max(block_epochs) + 1): + current_blocks = [i for i, e in enumerate(block_epochs) if e == epoch] + assert len(current_blocks) <= spec.SLOTS_PER_EPOCH, 'Number of blocks does not fit into an epoch=' + str(epoch) + + justifying_blocks = [b for b in current_blocks if _should_justifiy_epoch( + parents, block_epochs, current_justifications, previous_justifications, b, epoch)] + + # There should be enough slots to propose all blocks that are required to justify the epoch + assert len(justifying_blocks) <= JUSTIFYING_SLOT_COUNT, \ + 'Not enough slots to accommodate blocks justifying epoch=' + str(epoch) + signed_blocks, anchor_tip = _advance_state_to_anchor_epoch(spec, genesis_state, anchor_epoch, debug) block_tips = [None for _ in range(0, len(block_epochs))] # Initialize with the anchor block block_tips[0] = anchor_tip - JUSTIFYING_SLOT = 2 * spec.SLOTS_PER_EPOCH // 3 + 1 - for epoch in range(anchor_epoch + 1, max(block_epochs) + 1): current_blocks = [i for i, e in enumerate(block_epochs) if e == epoch] if len(current_blocks) == 0: continue - assert len(current_blocks) <= spec.SLOTS_PER_EPOCH, 'Number of blocks does not fit into an epoch' - # Case 2. Blocks are from disjoint subtrees -- not supported yet assert len( set([a for i, a in enumerate(parents) if i in current_blocks])) == 1, 'Disjoint trees are not supported' @@ -946,16 +952,12 @@ def _generate_filter_block_tree(spec, genesis_state, block_epochs, parents, prev state = ancestor_tip.beacon_state attestations = ancestor_tip.attestations - if _should_be_justified(parents, block_epochs, current_justifications, previous_justifications, epoch): - common_prefix_len = JUSTIFYING_SLOT - else: - common_prefix_len = min(JUSTIFYING_SLOT, spec.SLOTS_PER_EPOCH - len(current_blocks)) - threshold_slot = spec.compute_start_slot_at_epoch(epoch) + common_prefix_len + justifying_blocks = [b for b in current_blocks if _should_justifiy_epoch( + parents, block_epochs, current_justifications, previous_justifications, b, epoch)] - # There should be enough slots to propose all blocks - assert (spec.SLOTS_PER_EPOCH - common_prefix_len) >= len( - current_blocks), "Unsatisfiable constraints: not enough slots to propose all blocks: " + str(current_blocks) + common_prefix_len = min(JUSTIFYING_SLOT, spec.SLOTS_PER_EPOCH - len(current_blocks)) + threshold_slot = spec.compute_start_slot_at_epoch(epoch) + common_prefix_len # Build the chain up to but excluding a block that will justify current checkpoint while (state.slot < threshold_slot): @@ -992,6 +994,10 @@ def _generate_filter_block_tree(spec, genesis_state, block_epochs, parents, prev # Randomly distribute blocks across slots rnd.shuffle(block_distribution) + # Move all blocks that require to justify current epoch to the end to increase the chance of justification + block_distribution = [b for b in block_distribution if b not in justifying_blocks] + block_distribution = block_distribution + justifying_blocks + for index, block in enumerate(block_distribution): slot = threshold_slot + index state = common_state.copy() From 3c5ca9d67f7edfa63b1be28a33e91d483bc01e1a Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Fri, 26 Apr 2024 22:06:49 +0400 Subject: [PATCH 055/111] Make justified epoch 1 check stricter --- .../test/phase0/fork_choice/test_sm_links_tree_model.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py index 9f270d9097..36c0f57f4d 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py @@ -1070,11 +1070,7 @@ def test_filter_block_tree_model(spec, state, model_params=None, debug=False, se anchor_epoch = block_epochs[0] # Ensure that there is no attempt to justify GENESIS_EPOCH + 1 as it is not supported by the protocol - for b in range(1, len(block_epochs)): - if block_epochs[b] == spec.GENESIS_EPOCH + 1: - assert not current_justifications[b], 'Justification of epoch 1 is not supported by the protocol' - if block_epochs[b] == spec.GENESIS_EPOCH + 2: - assert not previous_justifications[b], 'Justification of epoch 1 is not supported by the protocol' + assert store_justified_epoch != spec.GENESIS_EPOCH + 1, 'Justification of epoch 1 is not supported by the protocol' # Ensure that epoch(block) == epoch(parent) + 1 for b in range(1, len(block_epochs)): From 8b34ac8b270e92f3ea1ad27583701af86b7b2a1a Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Thu, 25 Apr 2024 20:44:32 +0400 Subject: [PATCH 056/111] Fixed bug in `is_leaf`` --- .../fork_choice_generated/model/minizinc/Block_cover3.mzn | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/generators/fork_choice_generated/model/minizinc/Block_cover3.mzn b/tests/generators/fork_choice_generated/model/minizinc/Block_cover3.mzn index 24be71e814..4845d4c88c 100644 --- a/tests/generators/fork_choice_generated/model/minizinc/Block_cover3.mzn +++ b/tests/generators/fork_choice_generated/model/minizinc/Block_cover3.mzn @@ -45,7 +45,7 @@ constraint forall(b in BLOCK)(es[b] <= curr_e); constraint forall(b in BLOCK)(get_vse(b) <= store_je); constraint exists(b in BLOCK)(get_vse(b) == store_je); -predicate is_leaf(var BLOCK: b) = not exists(child in BLOCK where child < max_block)(parents[child] == b); +predicate is_leaf(var BLOCK: b) = not exists(child in BLOCK where child > b /\ child <= max_block)(parents[child] == b); var BLOCK: target_block; var BLOCK: max_block; From 3ad01e4e047a9c9ba09abe165f729ad9bd8cd0c5 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Wed, 1 May 2024 04:13:02 +0400 Subject: [PATCH 057/111] temporary small refactoring --- .../fork_choice_generated/instance_generator.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/generators/fork_choice_generated/instance_generator.py b/tests/generators/fork_choice_generated/instance_generator.py index 628fcc4fcd..44a3c9efed 100644 --- a/tests/generators/fork_choice_generated/instance_generator.py +++ b/tests/generators/fork_choice_generated/instance_generator.py @@ -87,15 +87,15 @@ def prepare_fn() -> None: def make_cases_fn() -> Iterable[TestCase]: _test_fn = _import_block_tree_test_fn() - def test_fn(phase: str, preset: str, seed: int, sm_links, block_parents): + def test_fn(phase: str, preset: str, seed: int, solution): return _test_fn(generator_mode=True, phase=phase, preset=preset, bls_active=BLS_ACTIVE, debug=debug, seed=seed, - sm_links=sm_links, - block_parents=block_parents, + sm_links=solution['sm_links'], + block_parents=solution['block_parents'], with_attester_slashings=with_attester_slashings, with_invalid_messages=with_invalid_messages) @@ -112,8 +112,7 @@ def test_fn(phase: str, preset: str, seed: int, sm_links, block_parents): spec = spec_targets[preset_name][fork_name] mutation_generator = MutatorsGenerator( spec, seed, number_of_mutations, - lambda: test_fn(fork_name, preset_name, seed, - sm_links=solution['sm_links'], block_parents=solution['block_parents']), + lambda: test_fn(fork_name, preset_name, seed, solution), debug=debug) for j in range(1 + number_of_mutations): yield TestCase(fork_name=fork_name, From d2e029b9a2911829ab7ac6772fda0ea4cdd67ae5 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Wed, 1 May 2024 05:07:40 +0400 Subject: [PATCH 058/111] refactoring --- .../instance_generator.py | 152 ++++++++---------- 1 file changed, 69 insertions(+), 83 deletions(-) diff --git a/tests/generators/fork_choice_generated/instance_generator.py b/tests/generators/fork_choice_generated/instance_generator.py index 44a3c9efed..8d8660d83f 100644 --- a/tests/generators/fork_choice_generated/instance_generator.py +++ b/tests/generators/fork_choice_generated/instance_generator.py @@ -5,7 +5,7 @@ from eth2spec.gen_helpers.gen_base.gen_typing import TestCase, TestProvider from itertools import product from toolz.dicttoolz import merge -from typing import Iterable +from typing import Iterable, Callable from importlib import import_module from eth2spec.utils import bls from eth2spec.test.helpers.typing import SpecForkName, PresetBaseName @@ -23,6 +23,48 @@ presets = [MINIMAL] +def _create_providers(test_name: str, /, + forks: Iterable[SpecForkName], + presets: Iterable[PresetBaseName], + debug: bool, + initial_seed: int, + solutions, + number_of_variations: int, + number_of_mutations: int, + test_fn: Callable, + ) -> Iterable[TestProvider]: + def prepare_fn() -> None: + bls.use_milagro() + return + + def make_cases_fn() -> Iterable[TestCase]: + seeds = [initial_seed] + if number_of_variations > 1: + rnd = random.Random(initial_seed) + seeds = [rnd.randint(1, 10000) for _ in range(number_of_variations)] + seeds[0] = initial_seed + + for i, solution in enumerate(solutions): + for seed in seeds: + for fork_name in forks: + for preset_name in presets: + spec = spec_targets[preset_name][fork_name] + mutation_generator = MutatorsGenerator( + spec, seed, number_of_mutations, + lambda: test_fn(fork_name, preset_name, seed, solution), + debug=debug) + for j in range(1 + number_of_mutations): + yield TestCase(fork_name=fork_name, + preset_name=preset_name, + runner_name=GENERATOR_NAME, + handler_name=test_name, + suite_name='fork_choice', + case_name=test_name + '_' + str(i) + '_' + str(seed) + '_' + str(j), + case_fn=mutation_generator.next_test_case) + + yield TestProvider(prepare=prepare_fn, make_cases=make_cases_fn) + + def _import_block_tree_test_fn(): src = import_module('eth2spec.test.phase0.fork_choice.test_sm_links_tree_model') print("generating test vectors from tests source: %s" % src.__name__) @@ -80,50 +122,22 @@ def _create_block_tree_providers(test_name: str, /, number_of_mutations: int, with_attester_slashings: bool, with_invalid_messages: bool) -> Iterable[TestProvider]: - def prepare_fn() -> None: - bls.use_milagro() - return + _test_fn = _import_block_tree_test_fn() - def make_cases_fn() -> Iterable[TestCase]: - _test_fn = _import_block_tree_test_fn() - - def test_fn(phase: str, preset: str, seed: int, solution): - return _test_fn(generator_mode=True, - phase=phase, - preset=preset, - bls_active=BLS_ACTIVE, - debug=debug, - seed=seed, - sm_links=solution['sm_links'], - block_parents=solution['block_parents'], - with_attester_slashings=with_attester_slashings, - with_invalid_messages=with_invalid_messages) + def test_fn(phase: str, preset: str, seed: int, solution): + return _test_fn(generator_mode=True, + phase=phase, + preset=preset, + bls_active=BLS_ACTIVE, + debug=debug, + seed=seed, + sm_links=solution['sm_links'], + block_parents=solution['block_parents'], + with_attester_slashings=with_attester_slashings, + with_invalid_messages=with_invalid_messages) - seeds = [initial_seed] - if number_of_variations > 1: - rnd = random.Random(initial_seed) - seeds = [rnd.randint(1, 10000) for _ in range(number_of_variations)] - seeds[0] = initial_seed - - for i, solution in enumerate(solutions): - for seed in seeds: - for fork_name in forks: - for preset_name in presets: - spec = spec_targets[preset_name][fork_name] - mutation_generator = MutatorsGenerator( - spec, seed, number_of_mutations, - lambda: test_fn(fork_name, preset_name, seed, solution), - debug=debug) - for j in range(1 + number_of_mutations): - yield TestCase(fork_name=fork_name, - preset_name=preset_name, - runner_name=GENERATOR_NAME, - handler_name=test_name, - suite_name='fork_choice', - case_name=test_name + '_' + str(i) + '_' + str(seed) + '_' + str(j), - case_fn=mutation_generator.next_test_case) - - yield TestProvider(prepare=prepare_fn, make_cases=make_cases_fn) + yield from _create_providers( + test_name, forks, presets, debug, initial_seed, solutions, number_of_variations, number_of_mutations, test_fn) def _import_block_cover_test_fn(): @@ -215,46 +229,18 @@ def _create_block_cover_providers(test_name: str, /, solutions, number_of_variations: int, number_of_mutations: int) -> Iterable[TestProvider]: - def prepare_fn() -> None: - bls.use_milagro() - return - - def make_cases_fn() -> Iterable[TestCase]: - _test_fn = _import_block_cover_test_fn() - def test_fn(phase: str, preset: str, seed: int, solution): - return _test_fn(generator_mode=True, - phase=phase, - preset=preset, - bls_active=BLS_ACTIVE, - debug=debug, - seed=seed, - model_params=solution) - - seeds = [initial_seed] - if number_of_variations > 1: - rnd = random.Random(initial_seed) - seeds = [rnd.randint(1, 10000) for _ in range(number_of_variations)] - seeds[0] = initial_seed - - for i, solution in enumerate(solutions): - for seed in seeds: - for fork_name in forks: - for preset_name in presets: - spec = spec_targets[preset_name][fork_name] - mutation_generator = MutatorsGenerator( - spec, seed, number_of_mutations, - lambda: test_fn(fork_name, preset_name, seed, solution), - debug=debug) - for j in range(1 + number_of_mutations): - yield TestCase(fork_name=fork_name, - preset_name=preset_name, - runner_name=GENERATOR_NAME, - handler_name=test_name, - suite_name='fork_choice', - case_name=test_name + '_' + str(i) + '_' + str(seed) + '_' + str(j), - case_fn=mutation_generator.next_test_case) - - yield TestProvider(prepare=prepare_fn, make_cases=make_cases_fn) + _test_fn = _import_block_cover_test_fn() + def test_fn(phase: str, preset: str, seed: int, solution): + return _test_fn(generator_mode=True, + phase=phase, + preset=preset, + bls_active=BLS_ACTIVE, + debug=debug, + seed=seed, + model_params=solution) + + yield from _create_providers( + test_name, forks, presets, debug, initial_seed, solutions, number_of_variations, number_of_mutations, test_fn) if __name__ == "__main__": From d1dd31dfc346869a5a3e5f9f09d5ad9b6852942a Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Fri, 10 May 2024 12:17:30 +0400 Subject: [PATCH 059/111] Extend FC checks with filtered block weights --- .../eth2spec/test/helpers/fork_choice.py | 37 +++++++++++-------- .../fork_choice/test_sm_links_tree_model.py | 8 ++-- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py index 72f4569c2a..f4a6c2a73d 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py +++ b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py @@ -285,22 +285,29 @@ def output_head_check(spec, store, test_steps): }) -def output_store_checks(spec, store, test_steps): - test_steps.append({ - 'checks': { - 'time': int(store.time), - 'head': get_formatted_head_output(spec, store), - 'justified_checkpoint': { - 'epoch': int(store.justified_checkpoint.epoch), - 'root': encode_hex(store.justified_checkpoint.root), - }, - 'finalized_checkpoint': { - 'epoch': int(store.finalized_checkpoint.epoch), - 'root': encode_hex(store.finalized_checkpoint.root), - }, - 'proposer_boost_root': encode_hex(store.proposer_boost_root), +def output_store_checks(spec, store, test_steps, with_filtered_block_weights=False): + checks = { + 'time': int(store.time), + 'head': get_formatted_head_output(spec, store), + 'justified_checkpoint': { + 'epoch': int(store.justified_checkpoint.epoch), + 'root': encode_hex(store.justified_checkpoint.root), + }, + 'finalized_checkpoint': { + 'epoch': int(store.finalized_checkpoint.epoch), + 'root': encode_hex(store.finalized_checkpoint.root), + }, + 'proposer_boost_root': encode_hex(store.proposer_boost_root), + } + + if with_filtered_block_weights: + filtered_block_weights = { + encode_hex(filtered_block_root): int(spec.get_weight(store, filtered_block_root)) + for filtered_block_root in spec.get_filtered_block_tree(store).keys() } - }) + checks['filtered_block_weights'] = filtered_block_weights + + test_steps.append({'checks': checks}) def apply_next_epoch_with_attestations(spec, diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py index 36c0f57f4d..1b8623e16e 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_sm_links_tree_model.py @@ -30,6 +30,7 @@ add_attestation, add_attester_slashing, add_block, + output_store_checks, ) from eth2spec.test.helpers.state import ( state_transition_and_sign_block, @@ -897,6 +898,8 @@ def test_sm_links_tree_model(spec, if debug: print(' head: ' + _print_head(spec, store)) + output_store_checks(spec, store, test_steps, with_filtered_block_weights=True) + yield 'steps', test_steps @@ -1126,7 +1129,6 @@ def test_filter_block_tree_model(spec, state, model_params=None, debug=False, se # Ensure the store.justified_checkpoint.epoch is as expected assert store.justified_checkpoint.epoch == store_justified_epoch # Ensure the target block is in filtered_blocks - filtered_block_roots = list(spec.get_filtered_block_tree(store).keys()) target_block_root = spec.hash_tree_root(post_block_tips[target_block].beacon_state.latest_block_header) # Check predicates @@ -1157,8 +1159,8 @@ def test_filter_block_tree_model(spec, state, model_params=None, debug=False, se and (predicates['store_je_eq_zero'] or predicates['block_vse_eq_store_je'] or predicates['block_vse_plus_two_ge_curr_e'])): - assert target_block_root in filtered_block_roots + assert target_block_root in spec.get_filtered_block_tree(store).keys() - test_steps.append({'property_checks': {'filtered_block_roots': [str(r) for r in filtered_block_roots]}}) + output_store_checks(spec, store, test_steps, with_filtered_block_weights=True) yield 'steps', test_steps From 371288c2546043bf6f180ab28a87fcda4e977946 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Fri, 10 May 2024 20:20:59 +0400 Subject: [PATCH 060/111] Move instantiators into a separate dir --- .../instance_generator.py | 51 +- .../instantiators/block_cover.py | 309 ++++++++ .../instantiators/block_tree.py | 660 ++++++++++++++++++ .../instantiators/debug_helpers.py | 65 ++ .../instantiators/helpers.py | 218 ++++++ 5 files changed, 1271 insertions(+), 32 deletions(-) create mode 100644 tests/generators/fork_choice_generated/instantiators/block_cover.py create mode 100644 tests/generators/fork_choice_generated/instantiators/block_tree.py create mode 100644 tests/generators/fork_choice_generated/instantiators/debug_helpers.py create mode 100644 tests/generators/fork_choice_generated/instantiators/helpers.py diff --git a/tests/generators/fork_choice_generated/instance_generator.py b/tests/generators/fork_choice_generated/instance_generator.py index 8d8660d83f..8e4dacadc7 100644 --- a/tests/generators/fork_choice_generated/instance_generator.py +++ b/tests/generators/fork_choice_generated/instance_generator.py @@ -13,6 +13,8 @@ from ruamel.yaml import YAML from mutation_operators import mk_mutations, MutatorsGenerator import random +from instantiators.block_tree import yield_block_tree_test_case +from instantiators.block_cover import yield_block_cover_test_case BLS_ACTIVE = False @@ -65,12 +67,6 @@ def make_cases_fn() -> Iterable[TestCase]: yield TestProvider(prepare=prepare_fn, make_cases=make_cases_fn) -def _import_block_tree_test_fn(): - src = import_module('eth2spec.test.phase0.fork_choice.test_sm_links_tree_model') - print("generating test vectors from tests source: %s" % src.__name__) - return getattr(src, 'test_sm_links_tree_model') - - def _find_sm_link_solutions(anchor_epoch: int, number_of_epochs: int, number_of_links: int) -> Iterable[Iterable[tuple]]: @@ -122,30 +118,22 @@ def _create_block_tree_providers(test_name: str, /, number_of_mutations: int, with_attester_slashings: bool, with_invalid_messages: bool) -> Iterable[TestProvider]: - _test_fn = _import_block_tree_test_fn() - def test_fn(phase: str, preset: str, seed: int, solution): - return _test_fn(generator_mode=True, - phase=phase, - preset=preset, - bls_active=BLS_ACTIVE, - debug=debug, - seed=seed, - sm_links=solution['sm_links'], - block_parents=solution['block_parents'], - with_attester_slashings=with_attester_slashings, - with_invalid_messages=with_invalid_messages) + return yield_block_tree_test_case(generator_mode=True, + phase=phase, + preset=preset, + bls_active=BLS_ACTIVE, + debug=debug, + seed=seed, + sm_links=solution['sm_links'], + block_parents=solution['block_parents'], + with_attester_slashings=with_attester_slashings, + with_invalid_messages=with_invalid_messages) yield from _create_providers( test_name, forks, presets, debug, initial_seed, solutions, number_of_variations, number_of_mutations, test_fn) -def _import_block_cover_test_fn(): - src = import_module('eth2spec.test.phase0.fork_choice.test_sm_links_tree_model') - print("generating test vectors from tests source: %s" % src.__name__) - return getattr(src, 'test_filter_block_tree_model') - - def _find_block_cover_model_solutions(anchor_epoch: int, store_justified_epoch_equal_zero: bool, block_voting_source_epoch_equal_store_justified_epoch: bool, @@ -229,15 +217,14 @@ def _create_block_cover_providers(test_name: str, /, solutions, number_of_variations: int, number_of_mutations: int) -> Iterable[TestProvider]: - _test_fn = _import_block_cover_test_fn() def test_fn(phase: str, preset: str, seed: int, solution): - return _test_fn(generator_mode=True, - phase=phase, - preset=preset, - bls_active=BLS_ACTIVE, - debug=debug, - seed=seed, - model_params=solution) + return yield_block_cover_test_case(generator_mode=True, + phase=phase, + preset=preset, + bls_active=BLS_ACTIVE, + debug=debug, + seed=seed, + model_params=solution) yield from _create_providers( test_name, forks, presets, debug, initial_seed, solutions, number_of_variations, number_of_mutations, test_fn) diff --git a/tests/generators/fork_choice_generated/instantiators/block_cover.py b/tests/generators/fork_choice_generated/instantiators/block_cover.py new file mode 100644 index 0000000000..3c0cb9a81a --- /dev/null +++ b/tests/generators/fork_choice_generated/instantiators/block_cover.py @@ -0,0 +1,309 @@ +import random +from eth2spec.test.context import ( + spec_state_test, + with_altair_and_later, +) +from eth2spec.test.helpers.fork_choice import ( + on_tick_and_append_step, + tick_and_add_block, + output_store_checks, +) +from eth2spec.test.helpers.state import ( + transition_to, + next_slot, +) +from .helpers import ( + ProtocolMessage, + FCTestData, + BranchTip, +) +from .helpers import ( + advance_state_to_anchor_epoch, + produce_block, + attest_to_slot, +) + +def _should_justify_epoch(parents, current_justifications, previous_justifications, block) -> bool: + if current_justifications[block]: + return True + + # Check if any child of the block justifies the epoch + for c in (b for b, p in enumerate(parents) if p == block): + if previous_justifications[c]: + return True + + return False + + +def _generate_filter_block_tree(spec, genesis_state, block_epochs, parents, previous_justifications, + current_justifications, rnd: random.Random, debug) -> ([], []): + JUSTIFYING_SLOT = 2 * spec.SLOTS_PER_EPOCH // 3 + 1 + JUSTIFYING_SLOT_COUNT = spec.SLOTS_PER_EPOCH - JUSTIFYING_SLOT + + anchor_epoch = block_epochs[0] + + # Run constraint checks before starting to generate blocks + for epoch in range(anchor_epoch + 1, max(block_epochs) + 1): + current_blocks = [i for i, e in enumerate(block_epochs) if e == epoch] + assert len(current_blocks) <= spec.SLOTS_PER_EPOCH, 'Number of blocks does not fit into an epoch=' + str(epoch) + + justifying_blocks = [b for b in current_blocks if _should_justify_epoch( + parents, current_justifications, previous_justifications, b)] + + # There should be enough slots to propose all blocks that are required to justify the epoch + assert len(justifying_blocks) <= JUSTIFYING_SLOT_COUNT, \ + 'Not enough slots to accommodate blocks justifying epoch=' + str(epoch) + + signed_blocks, anchor_tip = advance_state_to_anchor_epoch(spec, genesis_state, anchor_epoch, debug) + + block_tips = [None for _ in range(0, len(block_epochs))] + # Initialize with the anchor block + block_tips[0] = anchor_tip + + for epoch in range(anchor_epoch + 1, max(block_epochs) + 1): + current_blocks = [i for i, e in enumerate(block_epochs) if e == epoch] + if len(current_blocks) == 0: + continue + + # Case 2. Blocks are from disjoint subtrees -- not supported yet + assert len( + set([a for i, a in enumerate(parents) if i in current_blocks])) == 1, 'Disjoint trees are not supported' + + # Case 1. Blocks have common ancestor + a = parents[current_blocks[0]] + ancestor_tip = block_tips[a].copy() + + state = ancestor_tip.beacon_state + attestations = ancestor_tip.attestations + + justifying_blocks = [b for b in current_blocks if _should_justify_epoch( + parents, current_justifications, previous_justifications, b)] + + common_prefix_len = min(JUSTIFYING_SLOT, spec.SLOTS_PER_EPOCH - len(current_blocks)) + threshold_slot = spec.compute_start_slot_at_epoch(epoch) + common_prefix_len + + # Build the chain up to but excluding a block that will justify current checkpoint + while (state.slot < threshold_slot): + # Do not include attestations into blocks + if (state.slot < spec.compute_start_slot_at_epoch(epoch)): + new_block, state, _, _ = produce_block(spec, state, []) + signed_blocks.append(new_block) + else: + # Prevent previous epoch from being accidentally justified + # by filtering out previous epoch attestations + curr_epoch_attestations = [a for a in attestations if + epoch == spec.compute_epoch_at_slot(a.data.slot)] + other_attestations = [a for a in attestations if a not in curr_epoch_attestations] + new_block, state, curr_epoch_attestations, _ = produce_block(spec, state, curr_epoch_attestations) + attestations = other_attestations + curr_epoch_attestations + signed_blocks.append(new_block) + + # Attest + curr_slot_attestations = attest_to_slot(spec, state, state.slot) + attestations = curr_slot_attestations + attestations + + # Next slot + next_slot(spec, state) + + common_state = state + + # Assumption: one block is enough to satisfy previous_justifications[b] and current_justifications[b], + # i.e. block capacity is enough to accommodate attestations to justify previus and current epoch checkpoints + # if that needed. Considering that most of attestations were already included into the common chain prefix, + # we assume it is possible + empty_slot_count = spec.SLOTS_PER_EPOCH - common_prefix_len - len(current_blocks) + block_distribution = current_blocks.copy() + [-1 for _ in range(0, empty_slot_count)] + + # Randomly distribute blocks across slots + rnd.shuffle(block_distribution) + + # Move all blocks that require to justify current epoch to the end to increase the chance of justification + block_distribution = [b for b in block_distribution if b not in justifying_blocks] + block_distribution = block_distribution + justifying_blocks + + for index, block in enumerate(block_distribution): + slot = threshold_slot + index + state = common_state.copy() + + # Advance state to the slot + if state.slot < slot: + transition_to(spec, state, slot) + + # Propose a block if slot isn't empty + block_attestations = [] + if block > -1: + previous_epoch_attestations = [a for a in attestations if + epoch == spec.compute_epoch_at_slot(a.data.slot) + 1] + current_epoch_attestations = [a for a in attestations if + epoch == spec.compute_epoch_at_slot(a.data.slot)] + if (previous_justifications[block]): + block_attestations = block_attestations + previous_epoch_attestations + if (current_justifications[block]): + block_attestations = block_attestations + current_epoch_attestations + + # Propose block + new_block, state, _, _ = produce_block(spec, state, block_attestations) + signed_blocks.append(new_block) + + # Attest + # TODO pick a random tip to make attestation with if the slot is empty + curr_slot_attestations = attest_to_slot(spec, state, state.slot) + attestations = curr_slot_attestations + attestations + + # Next slot + next_slot(spec, state) + + if block > -1: + not_included_attestations = [a for a in attestations if a not in block_attestations] + + check_up_state = state.copy() + spec.process_justification_and_finalization(check_up_state) + + if current_justifications[block]: + assert check_up_state.current_justified_checkpoint.epoch == epoch, 'Unexpected current_jusitified_checkpoint.epoch: ' + str( + check_up_state.current_justified_checkpoint.epoch) + ' != ' + str(epoch) + elif previous_justifications[block]: + assert check_up_state.current_justified_checkpoint.epoch + 1 == epoch, 'Unexpected current_jusitified_checkpoint.epoch: ' + str( + check_up_state.current_justified_checkpoint.epoch) + ' != ' + str(epoch - 1) + + block_tips[block] = BranchTip(state, not_included_attestations, [*range(0, len(state.validators))], + check_up_state.current_justified_checkpoint) + + return signed_blocks, block_tips + + +def gen_block_cover_test_data(spec, state, model_params, debug, seed) -> (FCTestData, object): + anchor_state = state + anchor_block = spec.BeaconBlock(state_root=anchor_state.hash_tree_root()) + + print('\nseed:', seed) + print('model_params:', str(model_params)) + + block_epochs = model_params['block_epochs'] + parents = model_params['parents'] + previous_justifications = model_params['previous_justifications'] + current_justifications = model_params['current_justifications'] + + store_justified_epoch = model_params['store_justified_epoch'] + target_block = model_params['target_block'] + + anchor_epoch = block_epochs[0] + + # Ensure that there is no attempt to justify GENESIS_EPOCH + 1 as it is not supported by the protocol + assert store_justified_epoch != spec.GENESIS_EPOCH + 1, 'Justification of epoch 1 is not supported by the protocol' + + # Ensure that epoch(block) == epoch(parent) + 1 + for b in range(1, len(block_epochs)): + assert block_epochs[b] == block_epochs[parents[b]] + 1, 'epoch(' + str(b) + ') != epoch(' + str( + parents[b]) + ') + 1, block_epochs=' + str(block_epochs) + ', parents=' + str(parents) + + # Ensure that a descendant doesn't attempt to justify the previous epoch checkpoint + # if it has already been justified by its ancestor + for b in range(1, len(block_epochs)): + if previous_justifications[b]: + a = parents[b] + assert not current_justifications[a], str(b) + ' attempts to justify already justified epoch' + + rnd = random.Random(seed) + signed_blocks, post_block_tips = _generate_filter_block_tree(spec, + state, + block_epochs, + parents, + previous_justifications, + current_justifications, + rnd, + debug) + + # Meta data + meta = { + 'seed': seed, + 'model_params': model_params, + } + + blocks = [ProtocolMessage(block) for block in signed_blocks] + + test_data = FCTestData(meta, anchor_block, anchor_state, blocks, [], []) + target_block_root = spec.hash_tree_root(post_block_tips[target_block].beacon_state.latest_block_header) + + return test_data, target_block_root + + +def apply_final_steps(spec, store, model_params, test_steps): + current_epoch_slot = spec.compute_start_slot_at_epoch(model_params['current_epoch']) + current_epoch_time = store.genesis_time + current_epoch_slot * spec.config.SECONDS_PER_SLOT + if store.time < current_epoch_time: + on_tick_and_append_step(spec, store, current_epoch_time, test_steps) + + +def run_sanity_checks(spec, store, model_params, target_block_root): + current_epoch = spec.get_current_store_epoch(store) + # Ensure the epoch is correct + assert current_epoch == model_params['current_epoch'] + # Ensure the store.justified_checkpoint.epoch is as expected + assert store.justified_checkpoint.epoch == model_params['store_justified_epoch'] + + # Check predicates + predicates = model_params['predicates'] + if predicates['store_je_eq_zero']: + assert store.justified_checkpoint.epoch == spec.GENESIS_EPOCH, "store_je_eq_zero not satisfied" + + if predicates['block_is_leaf']: + assert not any( + b for b in store.blocks.values() if b.parent_root == target_block_root), "block_is_leaf not satisfied" + else: + assert any( + b for b in store.blocks.values() if b.parent_root == target_block_root), "block_is_leaf not satisfied" + + voting_source = spec.get_voting_source(store, target_block_root) + if predicates['block_vse_eq_store_je']: + assert voting_source.epoch == store.justified_checkpoint.epoch, "block_vse_eq_store_je not satisfied" + else: + assert voting_source.epoch != store.justified_checkpoint.epoch, "block_vse_eq_store_je not satisfied" + + if predicates['block_vse_plus_two_ge_curr_e']: + assert voting_source.epoch + 2 >= current_epoch, "block_vse_plus_two_ge_curr_e not satisfied" + else: + assert voting_source.epoch + 2 < current_epoch, "block_vse_plus_two_ge_curr_e not satisfied" + + # Ensure the target block is in filtered blocks if it is a leaf and eligible + if (predicates['block_is_leaf'] + and (predicates['store_je_eq_zero'] + or predicates['block_vse_eq_store_je'] + or predicates['block_vse_plus_two_ge_curr_e'])): + assert target_block_root in spec.get_filtered_block_tree(store).keys() + + +@with_altair_and_later +@spec_state_test +def yield_block_cover_test_case(spec, state, model_params=None, debug=False, seed=1): + test_data, target_block_root = gen_block_cover_test_data(spec, state, model_params, debug, seed) + + # Yield meta + for k, v in test_data.meta.items(): + yield k, 'meta', v + + # Yield anchor state and block initialization + yield 'anchor_state', test_data.anchor_state + yield 'anchor_block', test_data.anchor_block + + test_steps = [] + store = spec.get_forkchoice_store(test_data.anchor_state, test_data.anchor_block) + current_time = state.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time + on_tick_and_append_step(spec, store, current_time, test_steps) + assert store.time == current_time + + # Apply generated blocks + for block_message in test_data.blocks: + block = block_message.payload.message + yield from tick_and_add_block(spec, store, block_message.payload, test_steps) + block_root = block.hash_tree_root() + assert store.blocks[block_root] == block + + # Final steps (advance store to the current epoch) + apply_final_steps(spec, store, model_params, test_steps) + # Run sanity checks against model params + run_sanity_checks(spec, store, model_params, target_block_root) + + output_store_checks(spec, store, test_steps, with_filtered_block_weights=True) + + yield 'steps', test_steps diff --git a/tests/generators/fork_choice_generated/instantiators/block_tree.py b/tests/generators/fork_choice_generated/instantiators/block_tree.py new file mode 100644 index 0000000000..859aeac5b3 --- /dev/null +++ b/tests/generators/fork_choice_generated/instantiators/block_tree.py @@ -0,0 +1,660 @@ +import random +from eth2spec.test.context import ( + spec_state_test, + with_altair_and_later, +) +from eth2spec.test.helpers.attester_slashings import ( + get_valid_attester_slashing_by_indices, +) +from eth2spec.test.helpers.fork_choice import ( + on_tick_and_append_step, + add_attestation, + add_attester_slashing, + add_block, + output_store_checks, +) +from eth2spec.test.helpers.state import ( + transition_to, + next_slot, +) +from .helpers import ( + ProtocolMessage, + FCTestData, + BranchTip, +) +from .helpers import ( + advance_branch_to_next_epoch, + advance_state_to_anchor_epoch, + produce_block, + attest_to_slot, +) +from .debug_helpers import ( + attesters_in_block, + print_epoch, + print_head, + print_block_tree, +) + +MAX_JUSTIFICATION_RATE = 99 +MIN_JUSTIFICATION_RATE = 91 + +MAX_UNDERJUSTIFICATION_RATE = 65 +MIN_UNDERJUSTIFICATION_RATE = 55 + +EMPTY_SLOTS_RATE = 3 +MAX_TIPS_TO_ATTEST = 2 + +OFF_CHAIN_ATTESTATION_RATE = 10 +ON_CHAIN_ATTESTATION_RATE = 20 + +MAX_ATTESTER_SLASHINGS = 8 +ATTESTER_SLASHINGS_RATE = 8 +OFF_CHAIN_SLASHING_RATE = 33 +ON_CHAIN_SLASHING_RATE = 33 + +INVALID_MESSAGES_RATE = 5 + +class SmLink(tuple): + @property + def source(self): + return self[0] + + @property + def target(self): + return self[1] + + +def _justifying_participation_rate(rnd: random.Random): + """ + Should be high enough to ensure justification happens + """ + return rnd.randint(MIN_JUSTIFICATION_RATE, MAX_JUSTIFICATION_RATE) + + +def _under_justifying_participation_rate(rnd: random.Random): + return rnd.randint(MIN_UNDERJUSTIFICATION_RATE, MAX_UNDERJUSTIFICATION_RATE) + + +def _create_new_branch_tip(spec, branch_tips: dict[SmLink:BranchTip], sm_link: SmLink) -> BranchTip: + """ + Initialized a branch tip state for a new branch satisfying the given sm_link. + :return: a new branch tip. + """ + + # Find all forks with justified source + tips_with_justified_source = [s for s in branch_tips.values() + if s.eventually_justified_checkpoint.epoch == sm_link.source] + assert len(tips_with_justified_source) > 0 + + # Find and return the most adanced one + most_recent_tip = max(tips_with_justified_source, key=lambda s: s.beacon_state.slot) + return BranchTip(most_recent_tip.beacon_state.copy(), most_recent_tip.attestations.copy(), [], + most_recent_tip.eventually_justified_checkpoint) + + +def _sample_validator_partition(spec, state, epoch, participation_rate, rnd) -> []: + active_validator_indices = spec.get_active_validator_indices(state, epoch) + participants_count = len(active_validator_indices) * participation_rate // 100 + return rnd.sample(active_validator_indices, participants_count) + + +def _compute_validator_partitions(spec, branch_tips, current_links, current_epoch, rnd: random.Random) -> dict[ + SmLink:[int]]: + """ + Note: O(N) complex (N is a number of validators) and might be inefficient with large validator sets + + Uniformly distributes active validators between active forks specified by a given set of sm_links. + Handles two cases: + 1. Single active fork: + Randomly sample a single validator partition taking into account + whether the fork should have a justified checkpoint in the current epoch. + 2. Multiple active forks: + i. sample the majority partition if one of the forks is about to justify during the current epoch, + ii. run through a set of active validators and randomly select a fork for it, + do no consider validators that were sampled into the majority partition. + + Does not take into account validator's effective balance, based on assumption that the EB of every validator + is nearly the same. + + :return: [SmLink: participants] + """ + + justifying_links = [l for l in current_links if l.target == current_epoch] + + # Justifying conflicting checkpoints isn't supported + assert len(justifying_links) < 2 + justifying_link = justifying_links[0] if any(justifying_links) else None + + # Sanity check + for sm_link in current_links: + assert spec.get_current_epoch(branch_tips[sm_link].beacon_state) == current_epoch + + # Case when there is just one active fork + if len(current_links) == 1: + the_sm_link = current_links[0] + + if the_sm_link == justifying_link: + participation_rate = _justifying_participation_rate(rnd) + else: + participation_rate = _under_justifying_participation_rate(rnd) + + state = branch_tips[the_sm_link].beacon_state + participants = _sample_validator_partition(spec, state, current_epoch, participation_rate, rnd) + + return {the_sm_link: participants} + + # Cases with more than one active fork + participants = {l: [] for l in current_links} + + # Move the majority to the branch containing justification target + justifying_participants = [] + if justifying_link is not None: + state = branch_tips[justifying_link].beacon_state + justifying_participants = _sample_validator_partition(spec, + branch_tips[justifying_link].beacon_state, + current_epoch, + _justifying_participation_rate(rnd), + rnd) + + participants[justifying_link] = justifying_participants + + # Collect a set of active validator indexes across all forks + active_validator_per_branch = {} + all_active_validators = set() + for l in current_links: + state = branch_tips[l].beacon_state + active_validator_per_branch[l] = spec.get_active_validator_indices(state, current_epoch) + all_active_validators.update(active_validator_per_branch[l]) + + # Remove validators selected for justifying brach from the pool of active participants + all_active_validators = all_active_validators.difference(justifying_participants) + + # For each index: + # 1) Collect a set of branches where the validators is in active state (except for justifying branch) + # 2) Append the index to the list of participants for a randomly selected branch + for index in all_active_validators: + active_branches = [l for l in current_links if + index in active_validator_per_branch[l] and l not in justifying_links] + participants[tuple(rnd.choice(active_branches))].append(index) + + return participants + + +def _any_change_to_validator_partitions(spec, sm_links, current_epoch, anchor_epoch) -> bool: + """ + Returns ``true`` if validator partitions should be re-shuffled to advance branches in the current epoch: + 1. The first epoch after the anchor epoch always requires a new shuffling. + 2. Previous epoch has justified checkpoints. + Therefore, new supermajority links may need to be processed during the current epoch, + thus new block tree branches with new validator partitions may need to be created. + 3. Current epoch has justified checkpoint. + Therefore, the majority of validators must be moved to the justifying branch. + """ + assert current_epoch > anchor_epoch + + previous_epoch = current_epoch - 1 + + if previous_epoch == anchor_epoch: + return True + + for l in sm_links: + if l.target == current_epoch or l.target == previous_epoch: + return True + + return False + + +def _generate_sm_link_tree(spec, genesis_state, sm_links, rnd: random.Random, debug) -> ([], BranchTip): + """ + Generates a sequence of blocks satisfying a tree of supermajority links specified in the sm_links list, + i.e. a sequence of blocks with attestations required to create given supermajority links. + + The block generation strategy is to run through a span of epochs covered by the supermajority links + and for each epoch of the span apply the following steps: + 1. Obtain a list of supermajority links covering the epoch. + 2. Create a new block tree branch (fork) for every newly observed supermajority link. + 3. Randomly sample all active validators between a set of forks that are being advanced in the epoch. + Validator partitions are disjoint and are changing only at the epoch boundary. + If no new branches are created in the current epoch then partitions from the previous epoch will be used + to advahce the state of every fork to the next epoch. + 4. Advance every fork to the next epoch respecting a validator partition assigned to it in the current epoch. + Preserve attestations produced but not yet included on chain for potential inclusion in the next epoch. + 5. Justify required checkpoints by moving the majority of validators to the justifying fork, + this is taken into account by step (3). + + :return: Sequence of signed blocks oredered by a slot number. + """ + assert any(sm_links) + + # Find anchor epoch + anchor_epoch = min(sm_links, key=lambda l: l.source).source + + signed_blocks, anchor_tip = advance_state_to_anchor_epoch(spec, genesis_state, anchor_epoch, debug) + + # branch_tips hold the most recent state, validator partition and not included attestations for every fork + # Initialize branch tips with the anchor tip + anchor_link = SmLink((spec.GENESIS_EPOCH, anchor_epoch)) + branch_tips = {anchor_link: anchor_tip} + + highest_target_sm_link = max(sm_links, key=lambda l: l.target) + + # Finish at after the highest justified checkpoint + for current_epoch in range(anchor_epoch + 1, highest_target_sm_link.target + 1): + # Obtain sm links that span over the current epoch + current_epoch_sm_links = [l for l in sm_links if l.source < current_epoch <= l.target] + + # Initialize new forks + for l in (l for l in current_epoch_sm_links if branch_tips.get(l) is None): + new_branch_tip = _create_new_branch_tip(spec, branch_tips, l) + # Abort the test if any sm_links constraint appears to be unreachable + # because the justification of the source checkpoint hasn't been realized on chain yet + if l.target == current_epoch and new_branch_tip.beacon_state.current_justified_checkpoint.epoch < l.source: + return [], new_branch_tip + + branch_tips[l] = new_branch_tip + + # Reshuffle partitions if needed + if _any_change_to_validator_partitions(spec, sm_links, current_epoch, anchor_epoch): + partitions = _compute_validator_partitions(spec, branch_tips, current_epoch_sm_links, current_epoch, rnd) + for l in partitions.keys(): + old_tip_state = branch_tips[l] + new_tip_state = BranchTip(old_tip_state.beacon_state, old_tip_state.attestations, partitions[l], + old_tip_state.eventually_justified_checkpoint) + branch_tips[l] = new_tip_state + + # Debug checks + if debug: + print('\nepoch', str(current_epoch) + ':') + # Partitions are disjoint + for l1 in current_epoch_sm_links: + l1_participants = branch_tips[l1].participants + for l2 in current_epoch_sm_links: + if l1 != l2: + l2_participants = branch_tips[l2].participants + intersection = set(l1_participants).intersection(l2_participants) + assert len(intersection) == 0, \ + str(l1) + ' and ' + str(l2) + ' has common participants: ' + str(intersection) + + # Advance every branch taking into account attestations from past epochs and voting partitions + for sm_link in current_epoch_sm_links: + branch_tip = branch_tips[sm_link] + assert spec.get_current_epoch(branch_tip.beacon_state) == current_epoch, \ + 'Unexpected current_epoch(branch_tip.beacon_state): ' + str( + spec.get_current_epoch(branch_tip.beacon_state)) + ' != ' + str(current_epoch) + new_signed_blocks, new_branch_tip = advance_branch_to_next_epoch(spec, branch_tip) + + # Run sanity checks + post_state = new_branch_tip.beacon_state + assert spec.get_current_epoch(post_state) == current_epoch + 1, \ + 'Unexpected post_state epoch: ' + str(spec.get_current_epoch(post_state)) + ' != ' + str( + current_epoch + 1) + if sm_link.target == current_epoch: + assert post_state.previous_justified_checkpoint.epoch == sm_link.source, \ + 'Unexpected previous_justified_checkpoint.epoch: ' + str( + post_state.previous_justified_checkpoint.epoch) + ' != ' + str(sm_link.source) + assert new_branch_tip.eventually_justified_checkpoint.epoch == sm_link.target, \ + 'Unexpected eventually_justified_checkpoint.epoch: ' + str( + new_branch_tip.eventually_justified_checkpoint.epoch) + ' != ' + str(sm_link.target) + elif (sm_link.source != new_branch_tip.eventually_justified_checkpoint.epoch): + # Abort the test as the justification of the source checkpoint can't be realized on chain + # because of the lack of the block space + return [], new_branch_tip + + # If the fork won't be advanced in the future epochs + # ensure 1) all yet not included attestations are included on chain by advancing it to epoch N+1 + # 2) justification is realized by advancing it to epoch N+2 + is_fork_advanced_in_future = any((l for l in sm_links if l.source == sm_link.target)) + if sm_link.target == current_epoch and not is_fork_advanced_in_future: + advanced_branch_tip = new_branch_tip + + # Advance to N+1 if state.current_justified_checkpoint.epoch < eventually_justified_checkpoint.epoch + current_justified_epoch = new_branch_tip.beacon_state.current_justified_checkpoint.epoch + eventually_justified_epoch = new_branch_tip.eventually_justified_checkpoint.epoch + if current_justified_epoch < eventually_justified_epoch: + advanced_signed_blocks, advanced_branch_tip = advance_branch_to_next_epoch(spec, new_branch_tip, + enable_attesting=False) + new_signed_blocks = new_signed_blocks + advanced_signed_blocks + + # Build a block in the next epoch to justify the target on chain + state = advanced_branch_tip.beacon_state + while (spec.get_beacon_proposer_index(state) not in advanced_branch_tip.participants): + next_slot(spec, state) + + tip_block, _, _, _ = produce_block(spec, state, []) + new_signed_blocks.append(tip_block) + + assert state.current_justified_checkpoint.epoch == sm_link.target, \ + 'Unexpected state.current_justified_checkpoint: ' + str( + state.current_justified_checkpoint.epoch) + ' != ' + str(sm_link.target) + + # Debug output + if debug: + print('branch' + str(sm_link) + ':', + print_epoch(spec, branch_tips[sm_link].beacon_state, new_signed_blocks)) + print(' ', len(branch_tips[sm_link].participants), 'participants:', + new_branch_tip.participants) + print(' ', 'state.current_justified_checkpoint:', + '(epoch=' + str(post_state.current_justified_checkpoint.epoch) + + ', root=' + str(post_state.current_justified_checkpoint.root)[:6] + ')') + print(' ', 'eventually_justified_checkpoint:', + '(epoch=' + str(new_branch_tip.eventually_justified_checkpoint.epoch) + + ', root=' + str(new_branch_tip.eventually_justified_checkpoint.root)[:6] + ')') + + # Debug checks + if debug: + # Proposers are aligned with the partition + unexpected_proposers = [b.message.proposer_index for b in new_signed_blocks if + b.message.proposer_index not in branch_tip.participants] + assert len(unexpected_proposers) == 0, \ + 'Unexpected proposer: ' + str(unexpected_proposers[0]) + + # Attesters are aligned with the partition + current_epoch_state = branch_tips[sm_link].beacon_state + for b in new_signed_blocks: + # Attesting indexes from on chain attestations + attesters = attesters_in_block(spec, current_epoch_state, b, current_epoch) + # Attesting indexes from not yet included attestations + for a in new_branch_tip.attestations: + if a.data.target.epoch == current_epoch: + attesters.update( + spec.get_attesting_indices(current_epoch_state, a.data, a.aggregation_bits)) + unexpected_attesters = attesters.difference(branch_tip.participants) + assert len(unexpected_attesters) == 0, \ + 'Unexpected attester: ' + str(unexpected_attesters.pop()) + ', slot ' + str(b.message.slot) + + # Store the result + branch_tips[sm_link] = new_branch_tip + signed_blocks = signed_blocks + new_signed_blocks + + # Sort blocks by a slot + signed_block_messages = [ProtocolMessage(b) for b in signed_blocks] + return sorted(signed_block_messages, key=lambda b: b.payload.message.slot), branch_tips[highest_target_sm_link] + + +def _spoil_block(spec, rnd: random.Random, signed_block): + signed_block.message.state_root = spec.Root(rnd.randbytes(32)) + + +def _spoil_attester_slashing(spec, rnd: random.Random, attester_slashing): + attester_slashing.attestation_2.data = attester_slashing.attestation_1.data + + +def _spoil_attestation(spec, rnd: random.Random, attestation): + attestation.data.target.epoch = spec.GENESIS_EPOCH + + +def _generate_block_tree(spec, + anchor_tip: BranchTip, + rnd: random.Random, + debug, + block_parents, + with_attester_slashings, + with_invalid_messages) -> ([], [], []): + in_block_attestations = anchor_tip.attestations.copy() + post_states = [anchor_tip.beacon_state.copy()] + current_slot = anchor_tip.beacon_state.slot + block_index = 1 + block_tree_tips = set([0]) + in_block_attester_slashings = [] + attester_slashings_count = 0 + out_of_block_attestation_messages = [] + out_of_block_attester_slashing_messages = [] + signed_block_messages = [] + + while block_index < len(block_parents): + # Propose a block if slot shouldn't be empty + if rnd.randint(1, 100) > EMPTY_SLOTS_RATE: + # Advance parent state to the current slot + parent_index = block_parents[block_index] + parent_state = post_states[parent_index].copy() + transition_to(spec, parent_state, current_slot) + + # Filter out non-viable attestations + in_block_attestations = [a for a in in_block_attestations + if parent_state.slot <= a.data.slot + spec.SLOTS_PER_EPOCH] + + # Produce block + proposer = spec.get_beacon_proposer_index(parent_state) + if parent_state.validators[proposer].slashed or ( + with_invalid_messages and rnd.randint(0, 99) < INVALID_MESSAGES_RATE): + # Do not include attestations and slashings into invalid block + # as clients may opt in to process or not process attestations contained by invalid block + signed_block, _, _, _ = produce_block(spec, parent_state, [], []) + _spoil_block(spec, rnd, signed_block) + signed_block_messages.append(ProtocolMessage(signed_block, False)) + # Append the parent state as the post state as if the block were not applied + post_states.append(parent_state) + else: + signed_block, post_state, in_block_attestations, in_block_attester_slashings = produce_block( + spec, parent_state, in_block_attestations, in_block_attester_slashings) + + # Valid block + signed_block_messages.append(ProtocolMessage(signed_block, True)) + post_states.append(post_state) + + # Update tips + block_tree_tips.discard(parent_index) + block_tree_tips.add(block_index) + + # Next block + block_index += 1 + + # Attest to randomly selected tips + def split_list(lst, n): + k, m = divmod(len(lst), n) + return [lst[i * k + min(i, m):(i + 1) * k + min(i + 1, m)] for i in range(n)] + + attesting_tips = rnd.sample(sorted(block_tree_tips), min(len(block_tree_tips), MAX_TIPS_TO_ATTEST)) + validator_count = len(post_states[attesting_tips[0]].validators) + attesters = split_list([i for i in range(validator_count)], len(attesting_tips)) + for index, attesting_block_index in enumerate(attesting_tips): + # Advance the state to the current slot + attesting_state = post_states[attesting_block_index] + transition_to(spec, attesting_state, current_slot) + + # Attest to the block + attestations_in_slot = attest_to_slot(spec, attesting_state, attesting_state.slot, + lambda comm: [i for i in comm if i in attesters[index]]) + + # Sample on chain and off chain attestations + for a in attestations_in_slot: + choice = rnd.randint(0, 99) + if choice < OFF_CHAIN_ATTESTATION_RATE: + if with_invalid_messages and rnd.randint(0, 99) < INVALID_MESSAGES_RATE: + _spoil_attestation(spec, rnd, a) + attestation_message = ProtocolMessage(a, False) + else: + attestation_message = ProtocolMessage(a, True) + out_of_block_attestation_messages.append(attestation_message) + elif choice < OFF_CHAIN_ATTESTATION_RATE + ON_CHAIN_ATTESTATION_RATE: + in_block_attestations.insert(0, a) + else: + out_of_block_attestation_messages.append(ProtocolMessage(a, True)) + in_block_attestations.insert(0, a) + + # Create attester slashing + if with_attester_slashings and attester_slashings_count < MAX_ATTESTER_SLASHINGS: + if rnd.randint(0, 99) < ATTESTER_SLASHINGS_RATE: + state = post_states[attesting_tips[0]] + indices = [rnd.randint(0, len(state.validators) - 1)] + attester_slashing = get_valid_attester_slashing_by_indices(spec, state, indices, + slot=current_slot, + signed_1=True, + signed_2=True) + + choice = rnd.randint(0, 99) + if choice < OFF_CHAIN_SLASHING_RATE: + if with_invalid_messages and rnd.randint(0, 99) < INVALID_MESSAGES_RATE: + _spoil_attester_slashing(spec, rnd, attester_slashing) + attester_slashing_message = ProtocolMessage(attester_slashing, False) + else: + attester_slashing_message = ProtocolMessage(attester_slashing, True) + out_of_block_attester_slashing_messages.append(attester_slashing_message) + elif choice < OFF_CHAIN_SLASHING_RATE + ON_CHAIN_SLASHING_RATE: + in_block_attester_slashings.append(attester_slashing) + else: + out_of_block_attester_slashing_messages.append(ProtocolMessage(attester_slashing, True)) + in_block_attester_slashings.append(attester_slashing) + + attester_slashings_count += 1 + + # Next slot + current_slot += 1 + + if debug: + print('\nblock_tree:') + print('blocks: ', print_block_tree(spec, post_states[0], [b.payload for b in signed_block_messages])) + print(' ', 'state.current_justified_checkpoint:', + '(epoch=' + str(post_states[len(post_states) - 1].current_justified_checkpoint.epoch) + + ', root=' + str(post_states[len(post_states) - 1].current_justified_checkpoint.root)[:6] + ')') + + print('on_block:') + print(' ', 'count =', len(signed_block_messages)) + print(' ', 'valid =', len([b for b in signed_block_messages if b.valid])) + print('on_attestation:') + print(' ', 'count =', len(out_of_block_attestation_messages)) + print(' ', 'valid =', len([a for a in out_of_block_attestation_messages if a.valid])) + print('on_attester_slashing:') + print(' ', 'count =', len(out_of_block_attester_slashing_messages)) + print(' ', 'valid =', len([s for s in out_of_block_attester_slashing_messages if s.valid])) + + return (sorted(signed_block_messages, key=lambda b: b.payload.message.slot), + sorted(out_of_block_attestation_messages, key=lambda a: a.payload.data.slot), + sorted(out_of_block_attester_slashing_messages, key=lambda a: a.payload.attestation_1.data.slot)) + + +def _on_tick_and_append_step(spec, store, slot, test_steps): + time = slot * spec.config.SECONDS_PER_SLOT + store.genesis_time + on_tick_and_append_step(spec, store, time, test_steps) + assert store.time == time + + +def gen_block_tree_test_data(spec, + state, + debug, + seed, + sm_links, + block_parents, + with_attester_slashings, + with_invalid_messages) -> FCTestData: + assert (1, 2) not in sm_links, '(1, 2) sm link is not supported due to unsatisfiability' + sm_links = [SmLink(l) for l in sm_links] + + anchor_state = state + anchor_block = spec.BeaconBlock(state_root=anchor_state.hash_tree_root()) + + # Find a reachable solution trying with different seeds if needed + # sm_links constraints may not have a solution because of the randomization affecting validator partitions + signed_block_messages = [] + highest_tip = BranchTip(state, [], [], state.current_justified_checkpoint) + while True: + if debug: + print('\nseed:', seed) + print('sm_links:', sm_links) + print('block_parents:', block_parents) + + rnd = random.Random(seed) + signed_block_messages, highest_tip = _generate_sm_link_tree(spec, state, sm_links, rnd, debug) + if len(signed_block_messages) > 0: + break + + new_seed = rnd.randint(1, 10000) + print('\nUnsatisfiable constraints: sm_links: ' + str(sm_links) + ', seed=' + str( + seed) + ', will retry with seed=' + str(new_seed)) + seed = new_seed + + # Block tree model + block_tree, attestation_messages, attester_slashing_messages = _generate_block_tree( + spec, highest_tip, rnd, debug, block_parents, with_attester_slashings, with_invalid_messages) + + # Merge block_tree and sm_link_tree blocks + block_tree_root_slot = block_tree[0].payload.message.slot + signed_block_messages = [b for b in signed_block_messages if b.payload.message.slot < block_tree_root_slot] + signed_block_messages = signed_block_messages + block_tree + + # Meta data + meta = { + 'seed': seed, + 'sm_links': str(sm_links), + 'block_parents': str(block_parents), + } + + return FCTestData(meta, anchor_block, anchor_state, + signed_block_messages, attestation_messages, attester_slashing_messages) + + +@with_altair_and_later +@spec_state_test +def yield_block_tree_test_case(spec, + state, + debug=False, + seed=1, + sm_links=None, + block_parents=None, + with_attester_slashings=False, + with_invalid_messages=False): + # This test is mainly used for the test generation purposes + # Thus seed, sm_links and block_parents are provided by the generator + # Define sm_links, seed and block_parents explicitly to execute a certain run of this test + if sm_links is None or block_parents is None: + return + + test_data = gen_block_tree_test_data(spec, state, debug, seed, sm_links, block_parents, + with_attester_slashings, with_invalid_messages) + + # Yield meta + for k, v in test_data.meta.items(): + yield k, 'meta', v + + # Yield anchor state and block initialization + yield 'anchor_state', test_data.anchor_state + yield 'anchor_block', test_data.anchor_block + + test_steps = [] + store = spec.get_forkchoice_store(test_data.anchor_state, test_data.anchor_block) + _on_tick_and_append_step(spec, store, test_data.anchor_state.slot, test_steps) + + # Apply generated messages + max_block_slot = max(b.payload.message.slot for b in test_data.blocks) + max_attestation_slot = max(a.payload.data.slot for a in test_data.atts) if any(test_data.atts) else 0 + max_slashing_slot = max( + s.payload.attestation_1.data.slot for s in test_data.slashings) if any(test_data.slashings) else 0 + + start_slot = min(b.payload.message.slot for b in test_data.blocks) + end_slot = max(max_block_slot, max_attestation_slot, max_slashing_slot) + 1 + + # Advance time to start_slot + _on_tick_and_append_step(spec, store, start_slot, test_steps) + + # Apply messages to store + for slot in range(start_slot, end_slot + 1): + # on_tick + _on_tick_and_append_step(spec, store, slot, test_steps) + + # on_attestation for attestations from the previous slot + for attestation_message in (a for a in test_data.atts if a.payload.data.slot == slot - 1): + yield from add_attestation(spec, store, attestation_message.payload, + test_steps, valid=attestation_message.valid) + + # on_attester_slashing for slashing from the previous slot + for attester_slashing_message in (s for s in test_data.slashings + if s.payload.attestation_1.data.slot == slot - 1): + yield from add_attester_slashing(spec, store, attester_slashing_message.payload, + test_steps, valid=attester_slashing_message.valid) + + # on_block for blocks from the current slot + for signed_block_message in (b for b in test_data.blocks if b.payload.message.slot == slot): + yield from add_block(spec, store, signed_block_message.payload, test_steps, signed_block_message.valid) + + block_root = signed_block_message.payload.message.hash_tree_root() + if signed_block_message.valid: + assert store.blocks[block_root] == signed_block_message.payload.message + else: + assert block_root not in store.blocks.values() + + if debug: + print(' head: ' + print_head(spec, store)) + + output_store_checks(spec, store, test_steps, with_filtered_block_weights=True) + + yield 'steps', test_steps diff --git a/tests/generators/fork_choice_generated/instantiators/debug_helpers.py b/tests/generators/fork_choice_generated/instantiators/debug_helpers.py new file mode 100644 index 0000000000..1d008685e5 --- /dev/null +++ b/tests/generators/fork_choice_generated/instantiators/debug_helpers.py @@ -0,0 +1,65 @@ +from eth2spec.test.helpers.state import ( + transition_to, +) + + +def attesters_in_block(spec, epoch_state, signed_block, target_epoch): + block = signed_block.message + attesters = set() + for a in block.body.attestations: + if a.data.target.epoch == target_epoch: + attesters.update(spec.get_attesting_indices(epoch_state, a.data, a.aggregation_bits)) + return attesters + + +def print_block(spec, epoch_state, signed_block): + block = signed_block.message + if spec.get_current_epoch(epoch_state) > spec.GENESIS_EPOCH: + prev_attesters = attesters_in_block(spec, epoch_state, signed_block, spec.get_previous_epoch(epoch_state)) + else: + prev_attesters = set() + + curr_attesters = attesters_in_block(spec, epoch_state, signed_block, spec.get_current_epoch(epoch_state)) + prev_attester_str = 'a_prev=' + str(prev_attesters) if any(prev_attesters) else 'a_prev={}' + curr_attester_str = 'a_curr=' + str(curr_attesters) if any(curr_attesters) else 'a_curr={}' + + return 'b(r=' + str(spec.hash_tree_root(block))[:6] + ', p=' + str( + block.proposer_index) + ', ' + prev_attester_str + ', ' + curr_attester_str + ')' + + +def print_slot_range(spec, root_state, signed_blocks, start_slot, end_slot): + ret = "" + epoch_state = root_state.copy() + for slot in range(start_slot, end_slot): + transition_to(spec, epoch_state, slot) + blocks_in_slot = [b for b in signed_blocks if b.message.slot == slot] + if ret != "": + ret = ret + " <- " + if any(blocks_in_slot): + ret = ret + "s(" + str(slot) + ", " + print_block(spec, epoch_state, blocks_in_slot[0]) + ")" + else: + ret = ret + "s(" + str(slot) + ", _)" + + return ret + + +def print_epoch(spec, epoch_state, signed_blocks): + epoch = spec.get_current_epoch(epoch_state) + start_slot = spec.compute_start_slot_at_epoch(epoch) + return print_slot_range(spec, epoch_state, signed_blocks, start_slot, start_slot + spec.SLOTS_PER_EPOCH) + + +def print_block_tree(spec, root_state, signed_blocks): + start_slot = signed_blocks[0].message.slot + end_slot = signed_blocks[len(signed_blocks) - 1].message.slot + 1 + return print_slot_range(spec, root_state, signed_blocks, start_slot, end_slot) + + +def print_head(spec, store): + head = spec.get_head(store) + weight = spec.get_weight(store, head) + state = store.checkpoint_states[store.justified_checkpoint] + total_active_balance = spec.get_total_active_balance(state) + + return '(slot=' + str(store.blocks[head].slot) + ', root=' + str(head)[:6] + ', weight=' + str( + weight * 100 // total_active_balance) + '%)' diff --git a/tests/generators/fork_choice_generated/instantiators/helpers.py b/tests/generators/fork_choice_generated/instantiators/helpers.py new file mode 100644 index 0000000000..20157f0ccb --- /dev/null +++ b/tests/generators/fork_choice_generated/instantiators/helpers.py @@ -0,0 +1,218 @@ +from dataclasses import dataclass +from .debug_helpers import print_epoch +from eth2spec.test.helpers.state import ( + next_slot, +) +from eth2spec.test.helpers.attestations import ( + get_valid_attestation, +) +from eth2spec.test.helpers.block import ( + build_empty_block, + sign_block, +) + + +@dataclass +class ProtocolMessage: + payload: object + valid: bool = True + + +@dataclass +class FCTestData: + meta: dict + anchor_block: object + anchor_state: object + blocks: list[ProtocolMessage] + atts: list[ProtocolMessage] + slashings: list[ProtocolMessage] + + +class BranchTip: + def __init__(self, beacon_state, attestations, participants, eventually_justified_checkpoint): + self.beacon_state = beacon_state + self.attestations = attestations + self.participants = participants + self.eventually_justified_checkpoint = eventually_justified_checkpoint + + def copy(self): + return BranchTip(self.beacon_state.copy(), + self.attestations.copy(), + self.participants.copy(), + self.eventually_justified_checkpoint) + + +def _get_eligible_attestations(spec, state, attestations) -> []: + def _get_voting_source(target: spec.Checkpoint) -> spec.Checkpoint: + if target.epoch == spec.get_current_epoch(state): + return state.current_justified_checkpoint + else: + return state.previous_justified_checkpoint + + return [a for a in attestations if + state.slot <= a.data.slot + spec.SLOTS_PER_EPOCH + and a.data.source == _get_voting_source(a.data.target)] + + +def produce_block(spec, state, attestations, attester_slashings=[]): + """ + Produces a block including as many attestations as it is possible. + :return: Signed block, the post block state and attestations that were not included into the block. + """ + + # Filter out too old attestastions (TODO relax condition for Deneb) + eligible_attestations = _get_eligible_attestations(spec, state, attestations) + + # Prepare attestations + attestation_in_block = eligible_attestations[:spec.MAX_ATTESTATIONS] + + # Create a block with attestations + block = build_empty_block(spec, state) + for a in attestation_in_block: + block.body.attestations.append(a) + + # Add attester slashings + attester_slashings_in_block = attester_slashings[:spec.MAX_ATTESTER_SLASHINGS] + for s in attester_slashings_in_block: + block.body.attester_slashings.append(s) + + # Run state transition and sign off on a block + post_state = state.copy() + + valid = True + try: + spec.process_block(post_state, block) + except AssertionError: + valid = False + + block.state_root = post_state.hash_tree_root() + signed_block = sign_block(spec, post_state, block) + + # Filter out operations only if the block is valid + not_included_attestations = attestations + not_included_attester_slashings = attester_slashings + if valid: + not_included_attestations = [a for a in attestations if a not in attestation_in_block] + not_included_attester_slashings = [s for s in attester_slashings if s not in attester_slashings_in_block] + + # Return a pre state if the block is invalid + if not valid: + post_state = state + + return signed_block, post_state, not_included_attestations, not_included_attester_slashings + + +def attest_to_slot(spec, state, slot_to_attest, participants_filter=None) -> []: + """ + Creates attestation is a slot respecting participating validators. + :return: produced attestations + """ + + assert slot_to_attest <= state.slot + + committees_per_slot = spec.get_committee_count_per_slot(state, spec.compute_epoch_at_slot(slot_to_attest)) + attestations_in_slot = [] + for index in range(committees_per_slot): + beacon_committee = spec.get_beacon_committee(state, slot_to_attest, index) + participants = beacon_committee if participants_filter is None else participants_filter(beacon_committee) + if any(participants): + attestation = get_valid_attestation( + spec, + state, + slot_to_attest, + index=index, + signed=True, + filter_participant_set=participants_filter + ) + attestations_in_slot.append(attestation) + + return attestations_in_slot + + +def _compute_eventually_justified_epoch(spec, state, attestations, participants): + # If not all attestations are included on chain + # and attestation.data.target.epoch > beacon_state.current_justified_checkpoint.epoch + # compute eventually_justified_checkpoint, a would be state.current_justified_checkpoint if all attestations + # were included; this computation respects the validator partition that was building the branch + if len(attestations) > 0 \ + and attestations[0].data.target.epoch > state.current_justified_checkpoint.epoch \ + and attestations[0].data.target.epoch > spec.GENESIS_EPOCH: + branch_tip = BranchTip(state, attestations, participants, state.current_justified_checkpoint) + _, new_branch_tip = advance_branch_to_next_epoch(spec, branch_tip, enable_attesting=False) + + return new_branch_tip.beacon_state.current_justified_checkpoint + else: + return state.current_justified_checkpoint + + +def advance_branch_to_next_epoch(spec, branch_tip, enable_attesting=True): + """ + Advances a state of the block tree branch to the next epoch + respecting validators participating in building and attesting to this branch. + + The returned beacon state is advanced to the first slot of the next epoch while no block for that slot is created, + produced attestations that aren't yet included on chain are preserved for the future inclusion. + """ + + def participants_filter(comm): + return [index for index in comm if index in branch_tip.participants] + + signed_blocks = [] + attestations = branch_tip.attestations.copy() + state = branch_tip.beacon_state.copy() + current_epoch = spec.get_current_epoch(state) + target_slot = spec.compute_start_slot_at_epoch(current_epoch + 1) + + while state.slot < target_slot: + # Produce block if the proposer is among participanting validators + proposer = spec.get_beacon_proposer_index(state) + if state.slot > spec.GENESIS_SLOT and proposer in branch_tip.participants: + signed_block, state, attestations, _ = produce_block(spec, state, attestations) + signed_blocks.append(signed_block) + + if enable_attesting: + # Produce attestations + attestations_in_slot = attest_to_slot(spec, state, state.slot, participants_filter) + # And prepend them to the list + attestations = list(attestations_in_slot) + attestations + + # Advance a slot + next_slot(spec, state) + + # Cleanup attestations by removing outdated ones + attestations = [a for a in attestations if + a.data.target.epoch in (spec.get_previous_epoch(state), spec.get_current_epoch(state))] + + eventually_justified_checkpoint = _compute_eventually_justified_epoch(spec, state, attestations, + branch_tip.participants) + + return signed_blocks, BranchTip(state, attestations, branch_tip.participants, eventually_justified_checkpoint) + + +def advance_state_to_anchor_epoch(spec, state, anchor_epoch, debug) -> ([], BranchTip): + signed_blocks = [] + + genesis_tip = BranchTip(state.copy(), [], [*range(0, len(state.validators))], + state.current_justified_checkpoint) + + # Advance the state to the anchor_epoch + anchor_tip = genesis_tip + for epoch in range(spec.GENESIS_EPOCH, anchor_epoch + 1): + pre_state = anchor_tip.beacon_state + new_signed_blocks, anchor_tip = advance_branch_to_next_epoch(spec, anchor_tip) + signed_blocks = signed_blocks + new_signed_blocks + if debug: + post_state = anchor_tip.beacon_state + print('\nepoch', str(epoch) + ':') + print('branch(*, *):', print_epoch(spec, pre_state, new_signed_blocks)) + print(' ', len(anchor_tip.participants), 'participants:', anchor_tip.participants) + print(' ', 'state.current_justified_checkpoint:', + '(epoch=' + str(post_state.current_justified_checkpoint.epoch) + + ', root=' + str(post_state.current_justified_checkpoint.root)[:6] + ')') + print(' ', 'eventually_justified_checkpoint:', + '(epoch=' + str(anchor_tip.eventually_justified_checkpoint.epoch) + + ', root=' + str(anchor_tip.eventually_justified_checkpoint.root)[:6] + ')') + + return signed_blocks, anchor_tip + + From faee7d5a8243a2603d48bf7bf34ad7b44be57f67 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Sat, 11 May 2024 07:23:21 +0400 Subject: [PATCH 061/111] Unify test case instantiator --- .../instantiators/block_cover.py | 49 ++--------- .../instantiators/block_tree.py | 71 +--------------- .../instantiators/helpers.py | 83 ++++++++++++++++++- 3 files changed, 89 insertions(+), 114 deletions(-) diff --git a/tests/generators/fork_choice_generated/instantiators/block_cover.py b/tests/generators/fork_choice_generated/instantiators/block_cover.py index 3c0cb9a81a..2ac1a62e40 100644 --- a/tests/generators/fork_choice_generated/instantiators/block_cover.py +++ b/tests/generators/fork_choice_generated/instantiators/block_cover.py @@ -3,11 +3,6 @@ spec_state_test, with_altair_and_later, ) -from eth2spec.test.helpers.fork_choice import ( - on_tick_and_append_step, - tick_and_add_block, - output_store_checks, -) from eth2spec.test.helpers.state import ( transition_to, next_slot, @@ -21,6 +16,7 @@ advance_state_to_anchor_epoch, produce_block, attest_to_slot, + yield_fork_choice_test_case, ) def _should_justify_epoch(parents, current_justifications, previous_justifications, block) -> bool: @@ -222,23 +218,20 @@ def gen_block_cover_test_data(spec, state, model_params, debug, seed) -> (FCTest blocks = [ProtocolMessage(block) for block in signed_blocks] - test_data = FCTestData(meta, anchor_block, anchor_state, blocks, [], []) + current_epoch_slot = spec.compute_start_slot_at_epoch(model_params['current_epoch']) + current_epoch_time = state.genesis_time + current_epoch_slot * spec.config.SECONDS_PER_SLOT + + test_data = FCTestData(meta, anchor_block, anchor_state, blocks, store_final_time=current_epoch_time) target_block_root = spec.hash_tree_root(post_block_tips[target_block].beacon_state.latest_block_header) return test_data, target_block_root -def apply_final_steps(spec, store, model_params, test_steps): - current_epoch_slot = spec.compute_start_slot_at_epoch(model_params['current_epoch']) - current_epoch_time = store.genesis_time + current_epoch_slot * spec.config.SECONDS_PER_SLOT - if store.time < current_epoch_time: - on_tick_and_append_step(spec, store, current_epoch_time, test_steps) - - def run_sanity_checks(spec, store, model_params, target_block_root): current_epoch = spec.get_current_store_epoch(store) # Ensure the epoch is correct - assert current_epoch == model_params['current_epoch'] + assert current_epoch == model_params['current_epoch'], str(current_epoch) + ' != ' + str( + model_params['current_epoch']) # Ensure the store.justified_checkpoint.epoch is as expected assert store.justified_checkpoint.epoch == model_params['store_justified_epoch'] @@ -277,33 +270,7 @@ def run_sanity_checks(spec, store, model_params, target_block_root): @spec_state_test def yield_block_cover_test_case(spec, state, model_params=None, debug=False, seed=1): test_data, target_block_root = gen_block_cover_test_data(spec, state, model_params, debug, seed) - - # Yield meta - for k, v in test_data.meta.items(): - yield k, 'meta', v - - # Yield anchor state and block initialization - yield 'anchor_state', test_data.anchor_state - yield 'anchor_block', test_data.anchor_block - - test_steps = [] store = spec.get_forkchoice_store(test_data.anchor_state, test_data.anchor_block) - current_time = state.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time - on_tick_and_append_step(spec, store, current_time, test_steps) - assert store.time == current_time - - # Apply generated blocks - for block_message in test_data.blocks: - block = block_message.payload.message - yield from tick_and_add_block(spec, store, block_message.payload, test_steps) - block_root = block.hash_tree_root() - assert store.blocks[block_root] == block - - # Final steps (advance store to the current epoch) - apply_final_steps(spec, store, model_params, test_steps) + yield from yield_fork_choice_test_case(spec, store, test_data, debug) # Run sanity checks against model params run_sanity_checks(spec, store, model_params, target_block_root) - - output_store_checks(spec, store, test_steps, with_filtered_block_weights=True) - - yield 'steps', test_steps diff --git a/tests/generators/fork_choice_generated/instantiators/block_tree.py b/tests/generators/fork_choice_generated/instantiators/block_tree.py index 859aeac5b3..410c91b518 100644 --- a/tests/generators/fork_choice_generated/instantiators/block_tree.py +++ b/tests/generators/fork_choice_generated/instantiators/block_tree.py @@ -6,13 +6,6 @@ from eth2spec.test.helpers.attester_slashings import ( get_valid_attester_slashing_by_indices, ) -from eth2spec.test.helpers.fork_choice import ( - on_tick_and_append_step, - add_attestation, - add_attester_slashing, - add_block, - output_store_checks, -) from eth2spec.test.helpers.state import ( transition_to, next_slot, @@ -27,11 +20,11 @@ advance_state_to_anchor_epoch, produce_block, attest_to_slot, + yield_fork_choice_test_case, ) from .debug_helpers import ( attesters_in_block, print_epoch, - print_head, print_block_tree, ) @@ -523,12 +516,6 @@ def split_list(lst, n): sorted(out_of_block_attester_slashing_messages, key=lambda a: a.payload.attestation_1.data.slot)) -def _on_tick_and_append_step(spec, store, slot, test_steps): - time = slot * spec.config.SECONDS_PER_SLOT + store.genesis_time - on_tick_and_append_step(spec, store, time, test_steps) - assert store.time == time - - def gen_block_tree_test_data(spec, state, debug, @@ -602,59 +589,5 @@ def yield_block_tree_test_case(spec, test_data = gen_block_tree_test_data(spec, state, debug, seed, sm_links, block_parents, with_attester_slashings, with_invalid_messages) - # Yield meta - for k, v in test_data.meta.items(): - yield k, 'meta', v - - # Yield anchor state and block initialization - yield 'anchor_state', test_data.anchor_state - yield 'anchor_block', test_data.anchor_block - - test_steps = [] store = spec.get_forkchoice_store(test_data.anchor_state, test_data.anchor_block) - _on_tick_and_append_step(spec, store, test_data.anchor_state.slot, test_steps) - - # Apply generated messages - max_block_slot = max(b.payload.message.slot for b in test_data.blocks) - max_attestation_slot = max(a.payload.data.slot for a in test_data.atts) if any(test_data.atts) else 0 - max_slashing_slot = max( - s.payload.attestation_1.data.slot for s in test_data.slashings) if any(test_data.slashings) else 0 - - start_slot = min(b.payload.message.slot for b in test_data.blocks) - end_slot = max(max_block_slot, max_attestation_slot, max_slashing_slot) + 1 - - # Advance time to start_slot - _on_tick_and_append_step(spec, store, start_slot, test_steps) - - # Apply messages to store - for slot in range(start_slot, end_slot + 1): - # on_tick - _on_tick_and_append_step(spec, store, slot, test_steps) - - # on_attestation for attestations from the previous slot - for attestation_message in (a for a in test_data.atts if a.payload.data.slot == slot - 1): - yield from add_attestation(spec, store, attestation_message.payload, - test_steps, valid=attestation_message.valid) - - # on_attester_slashing for slashing from the previous slot - for attester_slashing_message in (s for s in test_data.slashings - if s.payload.attestation_1.data.slot == slot - 1): - yield from add_attester_slashing(spec, store, attester_slashing_message.payload, - test_steps, valid=attester_slashing_message.valid) - - # on_block for blocks from the current slot - for signed_block_message in (b for b in test_data.blocks if b.payload.message.slot == slot): - yield from add_block(spec, store, signed_block_message.payload, test_steps, signed_block_message.valid) - - block_root = signed_block_message.payload.message.hash_tree_root() - if signed_block_message.valid: - assert store.blocks[block_root] == signed_block_message.payload.message - else: - assert block_root not in store.blocks.values() - - if debug: - print(' head: ' + print_head(spec, store)) - - output_store_checks(spec, store, test_steps, with_filtered_block_weights=True) - - yield 'steps', test_steps + yield from yield_fork_choice_test_case(spec, store, test_data, debug) diff --git a/tests/generators/fork_choice_generated/instantiators/helpers.py b/tests/generators/fork_choice_generated/instantiators/helpers.py index 20157f0ccb..79aaef370b 100644 --- a/tests/generators/fork_choice_generated/instantiators/helpers.py +++ b/tests/generators/fork_choice_generated/instantiators/helpers.py @@ -1,4 +1,4 @@ -from dataclasses import dataclass +from dataclasses import dataclass, field from .debug_helpers import print_epoch from eth2spec.test.helpers.state import ( next_slot, @@ -10,7 +10,14 @@ build_empty_block, sign_block, ) - +from eth2spec.test.helpers.fork_choice import ( + on_tick_and_append_step, + add_attestation, + add_attester_slashing, + add_block, + output_store_checks, +) +from .debug_helpers import print_head @dataclass class ProtocolMessage: @@ -24,8 +31,9 @@ class FCTestData: anchor_block: object anchor_state: object blocks: list[ProtocolMessage] - atts: list[ProtocolMessage] - slashings: list[ProtocolMessage] + atts: list[ProtocolMessage] = field(default_factory=list) + slashings: list[ProtocolMessage] = field(default_factory=list) + store_final_time: int = 0 class BranchTip: @@ -216,3 +224,70 @@ def advance_state_to_anchor_epoch(spec, state, anchor_epoch, debug) -> ([], Bran return signed_blocks, anchor_tip +def _on_tick_and_append_step(spec, store, slot, test_steps): + time = slot * spec.config.SECONDS_PER_SLOT + store.genesis_time + on_tick_and_append_step(spec, store, time, test_steps) + assert store.time == time + + +def yield_fork_choice_test_case(spec, store, test_data: FCTestData, debug: bool): + # Yield meta + for k, v in test_data.meta.items(): + yield k, 'meta', v + + # Yield anchor state and block initialization + yield 'anchor_state', test_data.anchor_state + yield 'anchor_block', test_data.anchor_block + + test_steps = [] + _on_tick_and_append_step(spec, store, test_data.anchor_state.slot, test_steps) + + # Apply generated messages + max_block_slot = max(b.payload.message.slot for b in test_data.blocks) + max_attestation_slot = max(a.payload.data.slot for a in test_data.atts) + 1 if any(test_data.atts) else 0 + max_slashing_slot = max(max(s.payload.attestation_1.data.slot, + s.payload.attestation_2.data.slot) + for s in test_data.slashings) + 1 if any(test_data.slashings) else 0 + + start_slot = min(b.payload.message.slot for b in test_data.blocks) + end_slot = max(max_block_slot, max_attestation_slot, max_slashing_slot) + + # Advance time to start_slot + _on_tick_and_append_step(spec, store, start_slot, test_steps) + + # Apply messages to store + for slot in range(start_slot, end_slot + 1): + # on_tick + _on_tick_and_append_step(spec, store, slot, test_steps) + + # on_attestation for attestations from the previous slot + for attestation_message in (a for a in test_data.atts if a.payload.data.slot == slot - 1): + yield from add_attestation(spec, store, attestation_message.payload, + test_steps, valid=attestation_message.valid) + + # on_attester_slashing for slashing from the previous slot + for attester_slashing_message in (s for s in test_data.slashings + if max(s.payload.attestation_1.data.slot, + s.payload.attestation_2.data.slot) == slot - 1): + yield from add_attester_slashing(spec, store, attester_slashing_message.payload, + test_steps, valid=attester_slashing_message.valid) + + # on_block for blocks from the current slot + for signed_block_message in (b for b in test_data.blocks if b.payload.message.slot == slot): + yield from add_block(spec, store, signed_block_message.payload, test_steps, signed_block_message.valid) + + block_root = signed_block_message.payload.message.hash_tree_root() + if signed_block_message.valid: + assert store.blocks[block_root] == signed_block_message.payload.message + else: + assert block_root not in store.blocks.values() + + if store.time < test_data.store_final_time: + on_tick_and_append_step(spec, store, test_data.store_final_time, test_steps) + + if debug: + print(' head: ' + print_head(spec, store)) + + output_store_checks(spec, store, test_steps, with_filtered_block_weights=True) + + yield 'steps', test_steps From fe5b0788f72a903b301c9c48aff6b16c43e13408 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Wed, 15 May 2024 20:03:39 +0400 Subject: [PATCH 062/111] new instance configurations added: small, smoke, standard --- .../generate_test_instances.py | 149 +++++++++++++++--- 1 file changed, 131 insertions(+), 18 deletions(-) diff --git a/tests/generators/fork_choice_generated/generate_test_instances.py b/tests/generators/fork_choice_generated/generate_test_instances.py index 931f4b4501..5825898a16 100644 --- a/tests/generators/fork_choice_generated/generate_test_instances.py +++ b/tests/generators/fork_choice_generated/generate_test_instances.py @@ -72,6 +72,9 @@ def solve_block_cover(anchor_epoch: int, assert number_of_solutions is not None result = instance.solve(nr_solutions=number_of_solutions) + if anchor_epoch == 0 and not store_justified_epoch_equal_zero: + return + for s in result.solution: max_block = s.max_block yield {'block_epochs': s.es[:max_block + 1], @@ -111,41 +114,151 @@ def generate_block_cover(params): # } gen_params = { - 'block_tree_test': { - 'out_path': 'block_tree.yaml', + ################### + # small instances # + ################### + + 'block_tree_tree_small': { + 'out_path': 'block_tree_tree_small.yaml', + 'models': ['sm_link', 'block_tree'], + 'params': [ + ({'anchor_epoch': 0, 'number_of_epochs': 4, 'number_of_links': 3}, {'number_of_blocks': 8, 'max_children': 2, 'number_of_solutions': 3}), + ([{'sm_links': [[0, 1], [0, 2], [2, 3], [3, 4]]}], {'number_of_blocks': 16, 'max_children': 2, 'number_of_solutions': 3}), + ] + }, + 'block_tree_other_small': { + 'out_path': 'block_tree_other_small.yaml', + 'models': ['sm_link', 'block_tree'], + 'params': [ + ([{'sm_links': [[0, 1], [0, 2], [2, 3], [3, 4]]}], {'number_of_blocks': 12, 'max_children': 2, 'number_of_solutions': 3}), + ] + }, + 'block_cover_small': { + 'out_path': 'block_cover_small.yaml', + 'models': ['block_cover'], + 'params': [ + ({'anchor_epoch': 0, 'number_of_solutions': 1},), + ({'anchor_epoch': 2, 'number_of_solutions': 1},), + ] + }, + + ################### + # smoke instances # + ################### + + 'block_tree_tree_smoke': { + 'out_path': 'block_tree_tree_smoke.yaml', + 'models': ['sm_link', 'block_tree'], + 'params': [ + ({'anchor_epoch': 0, 'number_of_epochs': 5, 'number_of_links': 3}, {'number_of_blocks': 16, 'max_children': 2, 'number_of_solutions': 2}), + ([{'sm_links': [[0, 1], [0, 2], [2, 3], [3, 4]]}], {'number_of_blocks': 5, 'max_children': 3, 'number_of_solutions': None}), + ([{'sm_links': [[0, 1], [0, 2], [2, 3], [3, 4]]}], {'number_of_blocks': 12, 'max_children': 2, 'number_of_solutions': 73}), + ] + }, + 'block_tree_tree_smoke_2': { + 'out_path': 'block_tree_tree_smoke_2.yaml', + 'models': ['sm_link', 'block_tree'], + 'params': [ + ({'anchor_epoch': 0, 'number_of_epochs': 6, 'number_of_links': 4}, {'number_of_blocks': 16, 'max_children': 2, 'number_of_solutions': 2}), + ([{'sm_links': [[0, 1], [0, 2], [2, 3], [3, 4]]}], {'number_of_blocks': 6, 'max_children': 4, 'number_of_solutions': None}), + ([{'sm_links': [[0, 1], [0, 2], [2, 3], [3, 4]]}], {'number_of_blocks': 12, 'max_children': 2, 'number_of_solutions': 283}), + ] + }, + 'block_tree_other_smoke': { + 'out_path': 'block_tree_other_smoke.yaml', 'models': ['sm_link', 'block_tree'], 'params': [ - ({'anchor_epoch': 0, 'number_of_epochs': 4, 'number_of_links': 2}, {'number_of_blocks': 16, 'max_children': 3, 'number_of_solutions': 4}), - ({'anchor_epoch': 0, 'number_of_epochs': 4, 'number_of_links': 3}, {'number_of_blocks': 4, 'max_children': 3, 'number_of_solutions': 4}), - ({'anchor_epoch': 0, 'number_of_epochs': 5, 'number_of_links': 4}, {'number_of_blocks': 4, 'max_children': 3, 'number_of_solutions': 4}), + ([{'sm_links': [[0, 1], [0, 2], [2, 3], [3, 4]]}], {'number_of_blocks': 12, 'max_children': 2, 'number_of_solutions': 4}), ] }, - 'attester_slashings_test': { - 'out_path': 'attester_slashings.yaml', + 'block_cover_smoke': { + 'out_path': 'block_cover_smoke.yaml', + 'models': ['block_cover'], + 'params': [ + ({'anchor_epoch': 0, 'number_of_solutions': 2},), + ({'anchor_epoch': 2, 'number_of_solutions': 2},), + ] + }, + + ###################### + # standard instances # + ###################### + + 'block_tree_tree': { + 'out_path': 'block_tree_tree.yaml', 'models': ['sm_link', 'block_tree'], 'params': [ - ([{'sm_links': [[0, 1], [0, 2], [2, 3], [3, 4]]}], {'number_of_blocks': 16, 'max_children': 3, 'number_of_solutions': 4}), - ({'anchor_epoch': 0, 'number_of_epochs': 4, 'number_of_links': 3}, {'number_of_blocks': 4, 'max_children': 3, 'number_of_solutions': 4}), - ({'anchor_epoch': 0, 'number_of_epochs': 5, 'number_of_links': 4}, {'number_of_blocks': 4, 'max_children': 3, 'number_of_solutions': 4}), + ({'anchor_epoch': 0, 'number_of_epochs': 6, 'number_of_links': 4}, {'number_of_blocks': 16, 'max_children': 2, 'number_of_solutions': 5}), + ([{'sm_links': [[0, 1], [0, 2], [2, 3], [3, 4]]}], {'number_of_blocks': 6, 'max_children': 4, 'number_of_solutions': None}), + ([{'sm_links': [[0, 1], [0, 2], [2, 3], [3, 4]]}], {'number_of_blocks': 7, 'max_children': 2, 'number_of_solutions': None}), + ([{'sm_links': [[0, 1], [0, 2], [2, 3], [3, 4]]}], {'number_of_blocks': 16, 'max_children': 2, 'number_of_solutions': 358}), ] }, - 'invalid_messages_test': { - 'out_path': 'invalid_messages.yaml', + 'block_tree_tree_2': { + 'out_path': 'block_tree_tree_2.yaml', 'models': ['sm_link', 'block_tree'], 'params': [ - ([{'sm_links': [[0, 1], [0, 2], [2, 3], [3, 4]]}], {'number_of_blocks': 16, 'max_children': 3, 'number_of_solutions': 4}), - ({'anchor_epoch': 0, 'number_of_epochs': 4, 'number_of_links': 3}, {'number_of_blocks': 4, 'max_children': 3, 'number_of_solutions': 4}), - ({'anchor_epoch': 0, 'number_of_epochs': 5, 'number_of_links': 4}, {'number_of_blocks': 4, 'max_children': 3, 'number_of_solutions': 4}), + ({'anchor_epoch': 0, 'number_of_epochs': 6, 'number_of_links': 4}, {'number_of_blocks': 16, 'max_children': 2, 'number_of_solutions': 5}), + ([{'sm_links': [[0, 1], [0, 2], [2, 3], [3, 4]]}], {'number_of_blocks': 6, 'max_children': 4, 'number_of_solutions': None}), + ([{'sm_links': [[0, 1], [0, 2], [2, 3], [3, 4]]}], {'number_of_blocks': 7, 'max_children': 3, 'number_of_solutions': None}), + ([{'sm_links': [[0, 1], [0, 2], [2, 3], [3, 4]]}], {'number_of_blocks': 16, 'max_children': 2, 'number_of_solutions': 3101}), + ] + }, + 'block_tree_other': { + 'out_path': 'block_tree_other.yaml', + 'models': ['sm_link', 'block_tree'], + 'params': [ + ([{'sm_links': [[0, 1], [0, 2], [2, 3], [3, 4]]}], {'number_of_blocks': 12, 'max_children': 2, 'number_of_solutions': 8}), ] }, 'block_cover': { 'out_path': 'block_cover.yaml', 'models': ['block_cover'], 'params': [ - ({'anchor_epoch': 0, 'number_of_solutions': 1},), - ({'anchor_epoch': 2, 'number_of_solutions': 1},), + ({'anchor_epoch': 0, 'number_of_solutions': 2},), + ({'anchor_epoch': 2, 'number_of_solutions': 5},), ] - } + }, + + + ############# + # old stuff # + ############# + + # 'attester_slashings_test': { + # 'out_path': 'attester_slashings.yaml', + # 'models': ['sm_link', 'block_tree'], + # 'params': [ + # ([{'sm_links': [[0, 1], [0, 2], [2, 3], [3, 4]]}], {'number_of_blocks': 16, 'max_children': 3, 'number_of_solutions': 4}), + # ({'anchor_epoch': 0, 'number_of_epochs': 4, 'number_of_links': 3}, {'number_of_blocks': 4, 'max_children': 3, 'number_of_solutions': 4}), + # ({'anchor_epoch': 0, 'number_of_epochs': 5, 'number_of_links': 4}, {'number_of_blocks': 4, 'max_children': 3, 'number_of_solutions': 4}), + # ] + # }, + # 'invalid_messages_test': { + # 'out_path': 'invalid_messages.yaml', + # 'models': ['sm_link', 'block_tree'], + # 'params': [ + # ([{'sm_links': [[0, 1], [0, 2], [2, 3], [3, 4]]}], {'number_of_blocks': 16, 'max_children': 3, 'number_of_solutions': 4}), + # ({'anchor_epoch': 0, 'number_of_epochs': 4, 'number_of_links': 3}, {'number_of_blocks': 4, 'max_children': 3, 'number_of_solutions': 4}), + # ({'anchor_epoch': 0, 'number_of_epochs': 5, 'number_of_links': 4}, {'number_of_blocks': 4, 'max_children': 3, 'number_of_solutions': 4}), + # ] + # }, + # 'block_cover_1': { + # 'out_path': 'block_cover_1.yaml', + # 'models': ['block_cover'], + # 'params': [ + # ({'anchor_epoch': 0, 'number_of_solutions': 1},), + # ({'anchor_epoch': 2, 'number_of_solutions': 1},), + # ] + # }, + # 'block_cover_100': { + # 'out_path': 'block_cover_100.yaml', + # 'models': ['block_cover'], + # 'params': [ + # ({'anchor_epoch': 0, 'number_of_solutions': 100},), + # ({'anchor_epoch': 2, 'number_of_solutions': 100},), + # ] + # } } From 909a9f534c0955c2add08f2b5aa96672bff71e85 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Wed, 15 May 2024 20:05:00 +0400 Subject: [PATCH 063/111] new testgen configs added: small, smoke and standard --- .../fork_choice_generated/tg_small.yaml | 32 +++++++++++++++++++ .../fork_choice_generated/tg_smoke.yaml | 32 +++++++++++++++++++ .../fork_choice_generated/tg_standard.yaml | 30 ++++++++++------- 3 files changed, 82 insertions(+), 12 deletions(-) create mode 100644 tests/generators/fork_choice_generated/tg_small.yaml create mode 100644 tests/generators/fork_choice_generated/tg_smoke.yaml diff --git a/tests/generators/fork_choice_generated/tg_small.yaml b/tests/generators/fork_choice_generated/tg_small.yaml new file mode 100644 index 0000000000..297f62849d --- /dev/null +++ b/tests/generators/fork_choice_generated/tg_small.yaml @@ -0,0 +1,32 @@ +block_tree_test: + test_type: block_tree + instances: block_tree_tree_small.yaml # 12 + seed: 123 + nr_variations: 2 + nr_mutations: 1 +block_weight_test: + test_type: block_tree + instances: block_tree_other_small.yaml # 3 + seed: 123 + nr_variations: 5 + nr_mutations: 0 +attester_slashing_test: + test_type: block_tree + instances: block_tree_other_small.yaml # 3 + seed: 123 + nr_variations: 2 + nr_mutations: 1 + with_attester_slashings: true +invalid_message_test: + test_type: block_tree + instances: block_tree_other_small.yaml # 3 + seed: 123 + nr_variations: 2 + nr_mutations: 1 + with_invalid_messages: true +block_cover_test: + test_type: block_cover + instances: block_cover_small.yaml # 12 + seed: 456 + nr_variations: 2 + nr_mutations: 1 diff --git a/tests/generators/fork_choice_generated/tg_smoke.yaml b/tests/generators/fork_choice_generated/tg_smoke.yaml new file mode 100644 index 0000000000..32dfe4dcd5 --- /dev/null +++ b/tests/generators/fork_choice_generated/tg_smoke.yaml @@ -0,0 +1,32 @@ +block_tree_test: + test_type: block_tree + instances: block_tree_tree_smoke.yaml # 128 + seed: 123 + nr_variations: 2 + nr_mutations: 1 +block_weight_test: + test_type: block_tree + instances: block_tree_other_smoke.yaml # 4 + seed: 123 + nr_variations: 32 + nr_mutations: 3 +attester_slashing_test: + test_type: block_tree + instances: block_tree_other_smoke.yaml # 4 + seed: 123 + nr_variations: 8 + nr_mutations: 3 + with_attester_slashings: true +invalid_message_test: + test_type: block_tree + instances: block_tree_other_smoke.yaml # 4 + seed: 123 + nr_variations: 16 + nr_mutations: 1 + with_invalid_messages: true +block_cover_test: + test_type: block_cover + instances: block_cover_smoke.yaml # 24 + seed: 456 + nr_variations: 2 + nr_mutations: 3 diff --git a/tests/generators/fork_choice_generated/tg_standard.yaml b/tests/generators/fork_choice_generated/tg_standard.yaml index fc424c3dba..a99ee7450c 100644 --- a/tests/generators/fork_choice_generated/tg_standard.yaml +++ b/tests/generators/fork_choice_generated/tg_standard.yaml @@ -1,26 +1,32 @@ block_tree_test: test_type: block_tree - instances: block_tree.yaml + instances: block_tree_tree.yaml # 1024 seed: 123 - nr_variations: 1 - nr_mutations: 1 + nr_variations: 1 # 2 + nr_mutations: 0 # 1 +block_weight_test: + test_type: block_tree + instances: block_tree_other.yaml # 8 + seed: 123 + nr_variations: 1 # 64 + nr_mutations: 0 # 7 attester_slashing_test: test_type: block_tree - instances: block_tree.yaml + instances: block_tree_other.yaml # 8 seed: 123 - nr_variations: 1 - nr_mutations: 1 + nr_variations: 1 # 16 + nr_mutations: 0 # 7 with_attester_slashings: true invalid_message_test: test_type: block_tree - instances: block_tree.yaml + instances: block_tree_other.yaml # 8 seed: 123 - nr_variations: 1 - nr_mutations: 1 + nr_variations: 1 # 32 + nr_mutations: 0 # 3 with_invalid_messages: true block_cover_test: test_type: block_cover - instances: block_cover.yaml + instances: block_cover.yaml # 60 seed: 456 - nr_variations: 1 - nr_mutations: 1 + nr_variations: 1 # 5 + nr_mutations: 0 # 9 From 51e68f0e682550b05dd6a3b12eca93bde8c9a4c7 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Wed, 15 May 2024 20:10:07 +0400 Subject: [PATCH 064/111] add block_cover instances --- .../fork_choice_generated/block_cover.yaml | 360 +++++++++++++++--- .../block_cover_small.yaml | 108 ++++++ .../block_cover_smoke.yaml | 216 +++++++++++ 3 files changed, 639 insertions(+), 45 deletions(-) create mode 100644 tests/generators/fork_choice_generated/block_cover_small.yaml create mode 100644 tests/generators/fork_choice_generated/block_cover_smoke.yaml diff --git a/tests/generators/fork_choice_generated/block_cover.yaml b/tests/generators/fork_choice_generated/block_cover.yaml index d04d3b1408..ae3dcf0289 100644 --- a/tests/generators/fork_choice_generated/block_cover.yaml +++ b/tests/generators/fork_choice_generated/block_cover.yaml @@ -1,3 +1,12 @@ +- block_epochs: [0] + current_epoch: 1 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: true} + previous_justifications: [false] + store_justified_epoch: 0 + target_block: 0 - block_epochs: [0] current_epoch: 1 current_justifications: [false] @@ -16,6 +25,24 @@ previous_justifications: [false, false] store_justified_epoch: 0 target_block: 0 +- block_epochs: [0, 1, 1] + current_epoch: 1 + current_justifications: [false, false, false] + parents: [0, 0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: true} + previous_justifications: [false, false, false] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [0] + current_epoch: 3 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: true} + previous_justifications: [false] + store_justified_epoch: 0 + target_block: 0 - block_epochs: [0] current_epoch: 3 current_justifications: [false] @@ -34,59 +61,50 @@ previous_justifications: [false, false] store_justified_epoch: 0 target_block: 0 -- block_epochs: [0, 1, 1] - current_epoch: 2 - current_justifications: [false, false, true] - parents: [0, 0, 0] - predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, - store_je_eq_zero: false} - previous_justifications: [false, false, false] - store_justified_epoch: 1 - target_block: 2 -- block_epochs: [0, 1, 1, 2, 2] - current_epoch: 2 - current_justifications: [false, true, false, false, false] - parents: [0, 0, 0, 1, 1] - predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, - store_je_eq_zero: false} - previous_justifications: [false, false, false, false, false] - store_justified_epoch: 1 - target_block: 1 -- block_epochs: [0, 1, 1] - current_epoch: 2 - current_justifications: [false, false, true] - parents: [0, 0, 0] - predicates: {block_is_leaf: true, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: true, - store_je_eq_zero: false} - previous_justifications: [false, false, false] - store_justified_epoch: 1 - target_block: 1 - block_epochs: [0, 1] - current_epoch: 2 - current_justifications: [false, true] + current_epoch: 3 + current_justifications: [false, false] parents: [0, 0] - predicates: {block_is_leaf: false, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: true, + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: true} + previous_justifications: [false, true] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [2] + current_epoch: 3 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, store_je_eq_zero: false} - previous_justifications: [false, false] - store_justified_epoch: 1 + previous_justifications: [false] + store_justified_epoch: 2 target_block: 0 -- block_epochs: [0, 1, 1] +- block_epochs: [2] current_epoch: 3 - current_justifications: [false, false, true] - parents: [0, 0, 0] - predicates: {block_is_leaf: true, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: false, + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, store_je_eq_zero: false} - previous_justifications: [false, false, false] - store_justified_epoch: 1 - target_block: 1 -- block_epochs: [0, 1] + previous_justifications: [false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2] current_epoch: 3 - current_justifications: [false, true] - parents: [0, 0] - predicates: {block_is_leaf: false, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: false, + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, store_je_eq_zero: false} - previous_justifications: [false, false] - store_justified_epoch: 1 + previous_justifications: [false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2] + current_epoch: 3 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false] + store_justified_epoch: 2 target_block: 0 - block_epochs: [2] current_epoch: 3 @@ -106,6 +124,78 @@ previous_justifications: [false, false] store_justified_epoch: 2 target_block: 0 +- block_epochs: [2, 3, 3] + current_epoch: 3 + current_justifications: [false, false, false] + parents: [0, 0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false, false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2, 3, 3, 3] + current_epoch: 3 + current_justifications: [false, false, false, false] + parents: [0, 0, 0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false, false, false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2, 3, 3, 3, 3] + current_epoch: 3 + current_justifications: [false, false, false, false, false] + parents: [0, 0, 0, 0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false, false, false, false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2, 3] + current_epoch: 3 + current_justifications: [false, false] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, true] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2] + current_epoch: 5 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2] + current_epoch: 5 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2] + current_epoch: 5 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2] + current_epoch: 5 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false] + store_justified_epoch: 2 + target_block: 0 - block_epochs: [2] current_epoch: 5 current_justifications: [false] @@ -124,6 +214,60 @@ previous_justifications: [false, false] store_justified_epoch: 2 target_block: 0 +- block_epochs: [2, 3, 3] + current_epoch: 5 + current_justifications: [false, false, false] + parents: [0, 0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, false, false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2, 3, 3, 3] + current_epoch: 5 + current_justifications: [false, false, false, false] + parents: [0, 0, 0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, false, false, false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2, 3, 3, 3, 3] + current_epoch: 5 + current_justifications: [false, false, false, false, false] + parents: [0, 0, 0, 0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, false, false, false, false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2, 3] + current_epoch: 5 + current_justifications: [false, false] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2, 3, 3] + current_epoch: 4 + current_justifications: [false, false, true] + parents: [0, 0, 0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false, false] + store_justified_epoch: 3 + target_block: 1 +- block_epochs: [2, 3, 3] + current_epoch: 4 + current_justifications: [false, false, true] + parents: [0, 0, 0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false, true] + store_justified_epoch: 3 + target_block: 1 - block_epochs: [2, 3, 3] current_epoch: 4 current_justifications: [false, false, true] @@ -133,6 +277,42 @@ previous_justifications: [false, false, false] store_justified_epoch: 3 target_block: 1 +- block_epochs: [2, 3, 3] + current_epoch: 4 + current_justifications: [false, false, true] + parents: [0, 0, 0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false, true] + store_justified_epoch: 3 + target_block: 1 +- block_epochs: [2, 3, 3] + current_epoch: 4 + current_justifications: [false, false, true] + parents: [0, 0, 0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false, false] + store_justified_epoch: 3 + target_block: 1 +- block_epochs: [2, 3] + current_epoch: 4 + current_justifications: [false, true] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false] + store_justified_epoch: 3 + target_block: 0 +- block_epochs: [2, 3] + current_epoch: 4 + current_justifications: [false, true] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, true] + store_justified_epoch: 3 + target_block: 0 - block_epochs: [2, 3] current_epoch: 4 current_justifications: [false, true] @@ -142,6 +322,42 @@ previous_justifications: [false, false] store_justified_epoch: 3 target_block: 0 +- block_epochs: [2, 3] + current_epoch: 4 + current_justifications: [false, true] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, true] + store_justified_epoch: 3 + target_block: 0 +- block_epochs: [2, 3] + current_epoch: 4 + current_justifications: [false, true] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false] + store_justified_epoch: 3 + target_block: 0 +- block_epochs: [2, 3, 3] + current_epoch: 5 + current_justifications: [false, false, true] + parents: [0, 0, 0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, false, false] + store_justified_epoch: 3 + target_block: 1 +- block_epochs: [2, 3, 3] + current_epoch: 5 + current_justifications: [false, false, true] + parents: [0, 0, 0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, false, true] + store_justified_epoch: 3 + target_block: 1 - block_epochs: [2, 3, 3] current_epoch: 5 current_justifications: [false, false, true] @@ -151,6 +367,60 @@ previous_justifications: [false, false, false] store_justified_epoch: 3 target_block: 1 +- block_epochs: [2, 3, 3] + current_epoch: 5 + current_justifications: [false, false, true] + parents: [0, 0, 0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, false, true] + store_justified_epoch: 3 + target_block: 1 +- block_epochs: [2, 3, 3] + current_epoch: 5 + current_justifications: [false, false, true] + parents: [0, 0, 0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, false, false] + store_justified_epoch: 3 + target_block: 1 +- block_epochs: [2, 3] + current_epoch: 5 + current_justifications: [false, true] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, false] + store_justified_epoch: 3 + target_block: 0 +- block_epochs: [2, 3] + current_epoch: 5 + current_justifications: [false, true] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, true] + store_justified_epoch: 3 + target_block: 0 +- block_epochs: [2, 3] + current_epoch: 5 + current_justifications: [false, true] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, false] + store_justified_epoch: 3 + target_block: 0 +- block_epochs: [2, 3] + current_epoch: 5 + current_justifications: [false, true] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, true] + store_justified_epoch: 3 + target_block: 0 - block_epochs: [2, 3] current_epoch: 5 current_justifications: [false, true] diff --git a/tests/generators/fork_choice_generated/block_cover_small.yaml b/tests/generators/fork_choice_generated/block_cover_small.yaml new file mode 100644 index 0000000000..aa6d88f7d4 --- /dev/null +++ b/tests/generators/fork_choice_generated/block_cover_small.yaml @@ -0,0 +1,108 @@ +- block_epochs: [0] + current_epoch: 1 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: true} + previous_justifications: [false] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [0, 1] + current_epoch: 1 + current_justifications: [false, false] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: true} + previous_justifications: [false, false] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [0] + current_epoch: 3 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: true} + previous_justifications: [false] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [0, 1] + current_epoch: 3 + current_justifications: [false, false] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: true} + previous_justifications: [false, false] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [2] + current_epoch: 3 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2, 3] + current_epoch: 3 + current_justifications: [false, false] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2] + current_epoch: 5 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2, 3] + current_epoch: 5 + current_justifications: [false, false] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2, 3, 3] + current_epoch: 4 + current_justifications: [false, false, true] + parents: [0, 0, 0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false, false] + store_justified_epoch: 3 + target_block: 1 +- block_epochs: [2, 3] + current_epoch: 4 + current_justifications: [false, true] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false] + store_justified_epoch: 3 + target_block: 0 +- block_epochs: [2, 3, 3] + current_epoch: 5 + current_justifications: [false, false, true] + parents: [0, 0, 0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, false, false] + store_justified_epoch: 3 + target_block: 1 +- block_epochs: [2, 3] + current_epoch: 5 + current_justifications: [false, true] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, false] + store_justified_epoch: 3 + target_block: 0 diff --git a/tests/generators/fork_choice_generated/block_cover_smoke.yaml b/tests/generators/fork_choice_generated/block_cover_smoke.yaml new file mode 100644 index 0000000000..6f8ad60ce6 --- /dev/null +++ b/tests/generators/fork_choice_generated/block_cover_smoke.yaml @@ -0,0 +1,216 @@ +- block_epochs: [0] + current_epoch: 1 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: true} + previous_justifications: [false] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [0] + current_epoch: 1 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: true} + previous_justifications: [false] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [0, 1] + current_epoch: 1 + current_justifications: [false, false] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: true} + previous_justifications: [false, false] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [0, 1, 1] + current_epoch: 1 + current_justifications: [false, false, false] + parents: [0, 0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: true} + previous_justifications: [false, false, false] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [0] + current_epoch: 3 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: true} + previous_justifications: [false] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [0] + current_epoch: 3 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: true} + previous_justifications: [false] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [0, 1] + current_epoch: 3 + current_justifications: [false, false] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: true} + previous_justifications: [false, false] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [0, 1] + current_epoch: 3 + current_justifications: [false, false] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: true} + previous_justifications: [false, true] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [2] + current_epoch: 3 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2] + current_epoch: 3 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2, 3] + current_epoch: 3 + current_justifications: [false, false] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2, 3, 3] + current_epoch: 3 + current_justifications: [false, false, false] + parents: [0, 0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false, false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2] + current_epoch: 5 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2] + current_epoch: 5 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2, 3] + current_epoch: 5 + current_justifications: [false, false] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2, 3, 3] + current_epoch: 5 + current_justifications: [false, false, false] + parents: [0, 0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, false, false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2, 3, 3] + current_epoch: 4 + current_justifications: [false, false, true] + parents: [0, 0, 0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false, false] + store_justified_epoch: 3 + target_block: 1 +- block_epochs: [2, 3, 3] + current_epoch: 4 + current_justifications: [false, false, true] + parents: [0, 0, 0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false, true] + store_justified_epoch: 3 + target_block: 1 +- block_epochs: [2, 3] + current_epoch: 4 + current_justifications: [false, true] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false] + store_justified_epoch: 3 + target_block: 0 +- block_epochs: [2, 3] + current_epoch: 4 + current_justifications: [false, true] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, true] + store_justified_epoch: 3 + target_block: 0 +- block_epochs: [2, 3, 3] + current_epoch: 5 + current_justifications: [false, false, true] + parents: [0, 0, 0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, false, false] + store_justified_epoch: 3 + target_block: 1 +- block_epochs: [2, 3, 3] + current_epoch: 5 + current_justifications: [false, false, true] + parents: [0, 0, 0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, false, true] + store_justified_epoch: 3 + target_block: 1 +- block_epochs: [2, 3] + current_epoch: 5 + current_justifications: [false, true] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, false] + store_justified_epoch: 3 + target_block: 0 +- block_epochs: [2, 3] + current_epoch: 5 + current_justifications: [false, true] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, true] + store_justified_epoch: 3 + target_block: 0 From 2a352b06514acc1dd2fa8f91997770ac9a3b81f6 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Wed, 15 May 2024 20:11:32 +0400 Subject: [PATCH 065/111] add block_tree_tree instances --- .../block_tree_tree.yaml | 2280 +++++ .../block_tree_tree_2.yaml | 8424 +++++++++++++++++ .../block_tree_tree_small.yaml | 37 + .../block_tree_tree_smoke.yaml | 312 + .../block_tree_tree_smoke_2.yaml | 1252 +++ 5 files changed, 12305 insertions(+) create mode 100644 tests/generators/fork_choice_generated/block_tree_tree.yaml create mode 100644 tests/generators/fork_choice_generated/block_tree_tree_2.yaml create mode 100644 tests/generators/fork_choice_generated/block_tree_tree_small.yaml create mode 100644 tests/generators/fork_choice_generated/block_tree_tree_smoke.yaml create mode 100644 tests/generators/fork_choice_generated/block_tree_tree_smoke_2.yaml diff --git a/tests/generators/fork_choice_generated/block_tree_tree.yaml b/tests/generators/fork_choice_generated/block_tree_tree.yaml new file mode 100644 index 0000000000..0a698b7aa7 --- /dev/null +++ b/tests/generators/fork_choice_generated/block_tree_tree.yaml @@ -0,0 +1,2280 @@ +- block_parents: &id002 [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1, 0] + sm_links: &id001 + - [0, 1] + - [0, 2] + - [0, 3] + - [0, 4] +- block_parents: &id003 [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 5, 4, 3, 2, 1, 0] + sm_links: *id001 +- block_parents: &id005 [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 5, 4, 3, 2, 1, 0] + sm_links: *id001 +- block_parents: &id006 [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 5, 4, 3, 2, 1, 0] + sm_links: *id001 +- block_parents: &id007 [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 5, 4, 3, 2, 1, 0] + sm_links: *id001 +- block_parents: *id002 + sm_links: &id004 + - [0, 1] + - [0, 2] + - [0, 3] + - [0, 5] +- block_parents: *id003 + sm_links: *id004 +- block_parents: *id005 + sm_links: *id004 +- block_parents: *id006 + sm_links: *id004 +- block_parents: *id007 + sm_links: *id004 +- block_parents: *id002 + sm_links: &id008 + - [0, 1] + - [0, 2] + - [0, 3] + - [1, 4] +- block_parents: *id003 + sm_links: *id008 +- block_parents: *id005 + sm_links: *id008 +- block_parents: *id006 + sm_links: *id008 +- block_parents: *id007 + sm_links: *id008 +- block_parents: *id002 + sm_links: &id009 + - [0, 1] + - [0, 2] + - [0, 3] + - [2, 4] +- block_parents: *id003 + sm_links: *id009 +- block_parents: *id005 + sm_links: *id009 +- block_parents: *id006 + sm_links: *id009 +- block_parents: *id007 + sm_links: *id009 +- block_parents: *id002 + sm_links: &id010 + - [0, 1] + - [0, 2] + - [0, 3] + - [3, 4] +- block_parents: *id003 + sm_links: *id010 +- block_parents: *id005 + sm_links: *id010 +- block_parents: *id006 + sm_links: *id010 +- block_parents: *id007 + sm_links: *id010 +- block_parents: *id002 + sm_links: &id011 + - [0, 1] + - [0, 2] + - [0, 3] + - [1, 5] +- block_parents: *id003 + sm_links: *id011 +- block_parents: *id005 + sm_links: *id011 +- block_parents: *id006 + sm_links: *id011 +- block_parents: *id007 + sm_links: *id011 +- block_parents: *id002 + sm_links: &id012 + - [0, 1] + - [0, 2] + - [0, 3] + - [2, 5] +- block_parents: *id003 + sm_links: *id012 +- block_parents: *id005 + sm_links: *id012 +- block_parents: *id006 + sm_links: *id012 +- block_parents: *id007 + sm_links: *id012 +- block_parents: *id002 + sm_links: &id013 + - [0, 1] + - [0, 2] + - [0, 3] + - [3, 5] +- block_parents: *id003 + sm_links: *id013 +- block_parents: *id005 + sm_links: *id013 +- block_parents: *id006 + sm_links: *id013 +- block_parents: *id007 + sm_links: *id013 +- block_parents: *id002 + sm_links: &id014 + - [0, 1] + - [0, 2] + - [1, 3] + - [1, 4] +- block_parents: *id003 + sm_links: *id014 +- block_parents: *id005 + sm_links: *id014 +- block_parents: *id006 + sm_links: *id014 +- block_parents: *id007 + sm_links: *id014 +- block_parents: *id002 + sm_links: &id015 + - [0, 1] + - [0, 2] + - [1, 3] + - [2, 4] +- block_parents: *id003 + sm_links: *id015 +- block_parents: *id005 + sm_links: *id015 +- block_parents: *id006 + sm_links: *id015 +- block_parents: *id007 + sm_links: *id015 +- block_parents: *id002 + sm_links: &id016 + - [0, 1] + - [0, 2] + - [1, 3] + - [3, 4] +- block_parents: *id003 + sm_links: *id016 +- block_parents: *id005 + sm_links: *id016 +- block_parents: *id006 + sm_links: *id016 +- block_parents: *id007 + sm_links: *id016 +- block_parents: *id002 + sm_links: &id017 + - [0, 1] + - [0, 2] + - [1, 3] + - [1, 5] +- block_parents: *id003 + sm_links: *id017 +- block_parents: *id005 + sm_links: *id017 +- block_parents: *id006 + sm_links: *id017 +- block_parents: *id007 + sm_links: *id017 +- block_parents: *id002 + sm_links: &id018 + - [0, 1] + - [0, 2] + - [1, 3] + - [2, 5] +- block_parents: *id003 + sm_links: *id018 +- block_parents: *id005 + sm_links: *id018 +- block_parents: *id006 + sm_links: *id018 +- block_parents: *id007 + sm_links: *id018 +- block_parents: *id002 + sm_links: &id019 + - [0, 1] + - [0, 2] + - [1, 3] + - [3, 5] +- block_parents: *id003 + sm_links: *id019 +- block_parents: *id005 + sm_links: *id019 +- block_parents: *id006 + sm_links: *id019 +- block_parents: *id007 + sm_links: *id019 +- block_parents: *id002 + sm_links: &id020 + - [0, 1] + - [0, 2] + - [2, 3] + - [2, 4] +- block_parents: *id003 + sm_links: *id020 +- block_parents: *id005 + sm_links: *id020 +- block_parents: *id006 + sm_links: *id020 +- block_parents: *id007 + sm_links: *id020 +- block_parents: *id002 + sm_links: &id021 + - [0, 1] + - [0, 2] + - [2, 3] + - [3, 4] +- block_parents: *id003 + sm_links: *id021 +- block_parents: *id005 + sm_links: *id021 +- block_parents: *id006 + sm_links: *id021 +- block_parents: *id007 + sm_links: *id021 +- block_parents: *id002 + sm_links: &id022 + - [0, 1] + - [0, 2] + - [2, 3] + - [2, 5] +- block_parents: *id003 + sm_links: *id022 +- block_parents: *id005 + sm_links: *id022 +- block_parents: *id006 + sm_links: *id022 +- block_parents: *id007 + sm_links: *id022 +- block_parents: *id002 + sm_links: &id023 + - [0, 1] + - [0, 2] + - [2, 3] + - [3, 5] +- block_parents: *id003 + sm_links: *id023 +- block_parents: *id005 + sm_links: *id023 +- block_parents: *id006 + sm_links: *id023 +- block_parents: *id007 + sm_links: *id023 +- block_parents: *id002 + sm_links: &id024 + - [0, 1] + - [0, 2] + - [0, 4] + - [0, 5] +- block_parents: *id003 + sm_links: *id024 +- block_parents: *id005 + sm_links: *id024 +- block_parents: *id006 + sm_links: *id024 +- block_parents: *id007 + sm_links: *id024 +- block_parents: *id002 + sm_links: &id025 + - [0, 1] + - [0, 2] + - [0, 4] + - [1, 5] +- block_parents: *id003 + sm_links: *id025 +- block_parents: *id005 + sm_links: *id025 +- block_parents: *id006 + sm_links: *id025 +- block_parents: *id007 + sm_links: *id025 +- block_parents: *id002 + sm_links: &id026 + - [0, 1] + - [0, 2] + - [0, 4] + - [2, 5] +- block_parents: *id003 + sm_links: *id026 +- block_parents: *id005 + sm_links: *id026 +- block_parents: *id006 + sm_links: *id026 +- block_parents: *id007 + sm_links: *id026 +- block_parents: *id002 + sm_links: &id027 + - [0, 1] + - [0, 2] + - [0, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id027 +- block_parents: *id005 + sm_links: *id027 +- block_parents: *id006 + sm_links: *id027 +- block_parents: *id007 + sm_links: *id027 +- block_parents: *id002 + sm_links: &id028 + - [0, 1] + - [0, 2] + - [1, 4] + - [1, 5] +- block_parents: *id003 + sm_links: *id028 +- block_parents: *id005 + sm_links: *id028 +- block_parents: *id006 + sm_links: *id028 +- block_parents: *id007 + sm_links: *id028 +- block_parents: *id002 + sm_links: &id029 + - [0, 1] + - [0, 2] + - [1, 4] + - [2, 5] +- block_parents: *id003 + sm_links: *id029 +- block_parents: *id005 + sm_links: *id029 +- block_parents: *id006 + sm_links: *id029 +- block_parents: *id007 + sm_links: *id029 +- block_parents: *id002 + sm_links: &id030 + - [0, 1] + - [0, 2] + - [1, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id030 +- block_parents: *id005 + sm_links: *id030 +- block_parents: *id006 + sm_links: *id030 +- block_parents: *id007 + sm_links: *id030 +- block_parents: *id002 + sm_links: &id031 + - [0, 1] + - [0, 2] + - [2, 4] + - [2, 5] +- block_parents: *id003 + sm_links: *id031 +- block_parents: *id005 + sm_links: *id031 +- block_parents: *id006 + sm_links: *id031 +- block_parents: *id007 + sm_links: *id031 +- block_parents: *id002 + sm_links: &id032 + - [0, 1] + - [0, 2] + - [2, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id032 +- block_parents: *id005 + sm_links: *id032 +- block_parents: *id006 + sm_links: *id032 +- block_parents: *id007 + sm_links: *id032 +- block_parents: *id002 + sm_links: &id033 + - [0, 1] + - [0, 3] + - [0, 4] + - [0, 5] +- block_parents: *id003 + sm_links: *id033 +- block_parents: *id005 + sm_links: *id033 +- block_parents: *id006 + sm_links: *id033 +- block_parents: *id007 + sm_links: *id033 +- block_parents: *id002 + sm_links: &id034 + - [0, 2] + - [0, 3] + - [0, 4] + - [0, 5] +- block_parents: *id003 + sm_links: *id034 +- block_parents: *id005 + sm_links: *id034 +- block_parents: *id006 + sm_links: *id034 +- block_parents: *id007 + sm_links: *id034 +- block_parents: *id002 + sm_links: &id035 + - [0, 1] + - [0, 3] + - [0, 4] + - [1, 5] +- block_parents: *id003 + sm_links: *id035 +- block_parents: *id005 + sm_links: *id035 +- block_parents: *id006 + sm_links: *id035 +- block_parents: *id007 + sm_links: *id035 +- block_parents: *id002 + sm_links: &id036 + - [0, 1] + - [0, 3] + - [0, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id036 +- block_parents: *id005 + sm_links: *id036 +- block_parents: *id006 + sm_links: *id036 +- block_parents: *id007 + sm_links: *id036 +- block_parents: *id002 + sm_links: &id037 + - [0, 1] + - [0, 3] + - [0, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id037 +- block_parents: *id005 + sm_links: *id037 +- block_parents: *id006 + sm_links: *id037 +- block_parents: *id007 + sm_links: *id037 +- block_parents: *id002 + sm_links: &id038 + - [0, 2] + - [0, 3] + - [0, 4] + - [2, 5] +- block_parents: *id003 + sm_links: *id038 +- block_parents: *id005 + sm_links: *id038 +- block_parents: *id006 + sm_links: *id038 +- block_parents: *id007 + sm_links: *id038 +- block_parents: *id002 + sm_links: &id039 + - [0, 2] + - [0, 3] + - [0, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id039 +- block_parents: *id005 + sm_links: *id039 +- block_parents: *id006 + sm_links: *id039 +- block_parents: *id007 + sm_links: *id039 +- block_parents: *id002 + sm_links: &id040 + - [0, 2] + - [0, 3] + - [0, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id040 +- block_parents: *id005 + sm_links: *id040 +- block_parents: *id006 + sm_links: *id040 +- block_parents: *id007 + sm_links: *id040 +- block_parents: *id002 + sm_links: &id041 + - [0, 1] + - [0, 3] + - [1, 4] + - [1, 5] +- block_parents: *id003 + sm_links: *id041 +- block_parents: *id005 + sm_links: *id041 +- block_parents: *id006 + sm_links: *id041 +- block_parents: *id007 + sm_links: *id041 +- block_parents: *id002 + sm_links: &id042 + - [0, 1] + - [0, 3] + - [1, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id042 +- block_parents: *id005 + sm_links: *id042 +- block_parents: *id006 + sm_links: *id042 +- block_parents: *id007 + sm_links: *id042 +- block_parents: *id002 + sm_links: &id043 + - [0, 1] + - [0, 3] + - [1, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id043 +- block_parents: *id005 + sm_links: *id043 +- block_parents: *id006 + sm_links: *id043 +- block_parents: *id007 + sm_links: *id043 +- block_parents: *id002 + sm_links: &id044 + - [0, 1] + - [0, 3] + - [3, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id044 +- block_parents: *id005 + sm_links: *id044 +- block_parents: *id006 + sm_links: *id044 +- block_parents: *id007 + sm_links: *id044 +- block_parents: *id002 + sm_links: &id045 + - [0, 1] + - [0, 3] + - [3, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id045 +- block_parents: *id005 + sm_links: *id045 +- block_parents: *id006 + sm_links: *id045 +- block_parents: *id007 + sm_links: *id045 +- block_parents: *id002 + sm_links: &id046 + - [0, 2] + - [0, 3] + - [2, 4] + - [2, 5] +- block_parents: *id003 + sm_links: *id046 +- block_parents: *id005 + sm_links: *id046 +- block_parents: *id006 + sm_links: *id046 +- block_parents: *id007 + sm_links: *id046 +- block_parents: *id002 + sm_links: &id047 + - [0, 2] + - [0, 3] + - [2, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id047 +- block_parents: *id005 + sm_links: *id047 +- block_parents: *id006 + sm_links: *id047 +- block_parents: *id007 + sm_links: *id047 +- block_parents: *id002 + sm_links: &id048 + - [0, 2] + - [0, 3] + - [2, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id048 +- block_parents: *id005 + sm_links: *id048 +- block_parents: *id006 + sm_links: *id048 +- block_parents: *id007 + sm_links: *id048 +- block_parents: *id002 + sm_links: &id049 + - [0, 2] + - [0, 3] + - [3, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id049 +- block_parents: *id005 + sm_links: *id049 +- block_parents: *id006 + sm_links: *id049 +- block_parents: *id007 + sm_links: *id049 +- block_parents: *id002 + sm_links: &id050 + - [0, 2] + - [0, 3] + - [3, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id050 +- block_parents: *id005 + sm_links: *id050 +- block_parents: *id006 + sm_links: *id050 +- block_parents: *id007 + sm_links: *id050 +- block_parents: *id002 + sm_links: &id051 + - [0, 1] + - [1, 3] + - [1, 4] + - [1, 5] +- block_parents: *id003 + sm_links: *id051 +- block_parents: *id005 + sm_links: *id051 +- block_parents: *id006 + sm_links: *id051 +- block_parents: *id007 + sm_links: *id051 +- block_parents: *id002 + sm_links: &id052 + - [0, 1] + - [1, 3] + - [1, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id052 +- block_parents: *id005 + sm_links: *id052 +- block_parents: *id006 + sm_links: *id052 +- block_parents: *id007 + sm_links: *id052 +- block_parents: *id002 + sm_links: &id053 + - [0, 1] + - [1, 3] + - [1, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id053 +- block_parents: *id005 + sm_links: *id053 +- block_parents: *id006 + sm_links: *id053 +- block_parents: *id007 + sm_links: *id053 +- block_parents: *id002 + sm_links: &id054 + - [0, 1] + - [1, 3] + - [3, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id054 +- block_parents: *id005 + sm_links: *id054 +- block_parents: *id006 + sm_links: *id054 +- block_parents: *id007 + sm_links: *id054 +- block_parents: *id002 + sm_links: &id055 + - [0, 1] + - [1, 3] + - [3, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id055 +- block_parents: *id005 + sm_links: *id055 +- block_parents: *id006 + sm_links: *id055 +- block_parents: *id007 + sm_links: *id055 +- block_parents: *id002 + sm_links: &id056 + - [0, 2] + - [2, 3] + - [2, 4] + - [2, 5] +- block_parents: *id003 + sm_links: *id056 +- block_parents: *id005 + sm_links: *id056 +- block_parents: *id006 + sm_links: *id056 +- block_parents: *id007 + sm_links: *id056 +- block_parents: *id002 + sm_links: &id057 + - [0, 2] + - [2, 3] + - [2, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id057 +- block_parents: *id005 + sm_links: *id057 +- block_parents: *id006 + sm_links: *id057 +- block_parents: *id007 + sm_links: *id057 +- block_parents: *id002 + sm_links: &id058 + - [0, 2] + - [2, 3] + - [2, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id058 +- block_parents: *id005 + sm_links: *id058 +- block_parents: *id006 + sm_links: *id058 +- block_parents: *id007 + sm_links: *id058 +- block_parents: *id002 + sm_links: &id059 + - [0, 2] + - [2, 3] + - [3, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id059 +- block_parents: *id005 + sm_links: *id059 +- block_parents: *id006 + sm_links: *id059 +- block_parents: *id007 + sm_links: *id059 +- block_parents: *id002 + sm_links: &id060 + - [0, 2] + - [2, 3] + - [3, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id060 +- block_parents: *id005 + sm_links: *id060 +- block_parents: *id006 + sm_links: *id060 +- block_parents: *id007 + sm_links: *id060 +- block_parents: [0, 0, 1, 0, 0, 0] + sm_links: &id061 + - [0, 1] + - [0, 2] + - [2, 3] + - [3, 4] +- block_parents: [0, 0, 0, 1, 0, 0] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 0, 0] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 0, 0] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 0, 0] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 1, 0] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 1, 0] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 1, 0] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 1, 0] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 1, 0] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 1, 0] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 2, 0] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 2, 0] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 2, 0] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 2, 0] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 2, 0] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 2, 0] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 3, 0] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 3, 0] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 3, 0] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 3, 0] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 3, 0] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 3, 0] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 0, 1] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 0, 1] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 0, 1] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 0, 1] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 0, 1] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 0, 1] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 1, 1] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 1, 1] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 1, 1] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 1, 1] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 1, 1] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 1, 1] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 2, 1] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 2, 1] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 2, 1] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 2, 1] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 2, 1] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 2, 1] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 3, 1] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 3, 1] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 3, 1] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 3, 1] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 3, 1] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 3, 1] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 0, 2] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 0, 2] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 0, 2] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 0, 2] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 0, 2] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 0, 2] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 1, 2] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 1, 2] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 1, 2] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 1, 2] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 1, 2] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 1, 2] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 2, 2] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 2, 2] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 2, 2] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 2, 2] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 2, 2] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 2, 2] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 3, 2] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 3, 2] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 3, 2] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 3, 2] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 3, 2] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 3, 2] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 0, 3] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 0, 3] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 0, 3] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 0, 3] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 0, 3] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 0, 3] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 1, 3] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 1, 3] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 1, 3] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 1, 3] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 1, 3] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 1, 3] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 2, 3] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 2, 3] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 2, 3] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 2, 3] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 2, 3] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 2, 3] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 3, 3] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 3, 3] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 3, 3] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 3, 3] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 3, 3] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 3, 3] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 0, 4] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 0, 4] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 0, 4] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 0, 4] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 0, 4] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 0, 4] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 1, 4] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 1, 4] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 1, 4] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 1, 4] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 1, 4] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 1, 4] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 2, 4] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 2, 4] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 2, 4] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 2, 4] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 2, 4] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 2, 4] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 3, 4] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 3, 4] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 3, 4] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 3, 4] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 3, 4] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 3, 4] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 2, 1, 0] + sm_links: &id062 + - [0, 1] + - [0, 2] + - [2, 3] + - [3, 4] +- block_parents: [0, 0, 1, 2, 3, 1, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 2, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 2, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 2, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 2, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 3, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 3, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 3, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 3, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 3, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 4, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 4, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 4, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 4, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 4, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 0, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 0, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 1, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 1, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 2, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 2, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 2, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 2, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 2, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 2, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 2, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 2, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 3, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 3, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 3, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 3, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 3, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 3, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 3, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 3, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 3, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 3, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 4, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 4, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 4, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 4, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 4, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 4, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 4, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 4, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 4, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 4, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 0, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 0, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 0, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 0, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 1, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 1, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 1, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 1, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 1, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 1, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 1, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 1, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 2, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 2, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 2, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 2, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 2, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 2, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 0, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 0, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 0, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 0, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 0, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 1, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 1, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 1, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 1, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 1, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 1, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 1, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 1, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 1, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 1, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 3, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 3, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 3, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 3, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 3, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 3, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 3, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 3, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 3, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 3, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 3, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 0, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 0, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 0, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 0, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 0, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 1, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 1, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 1, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 1, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 1, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 1, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 1, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 1, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 1, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 1, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 0, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 0, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 0, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 0, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 0, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 1, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 1, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 1, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 1, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 1, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 1, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 1, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 1, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 1, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 1, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1, 0] + sm_links: &id063 + - [0, 1] + - [0, 2] + - [2, 3] + - [3, 4] +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 5, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 5, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 5, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 5, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 6, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 6, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 6, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 6, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 6, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 6, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 6, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 6, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 6, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 5, 7, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 7, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 6, 7, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 7, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 7, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 7, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 7, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 7, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 7, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 7, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 7, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 7, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 7, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 7, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 5, 8, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 8, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 6, 8, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 8, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 8, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 8, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 8, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 8, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 8, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 8, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 8, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 8, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 8, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 5, 9, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 9, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 6, 9, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 9, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 9, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 9, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 9, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 9, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 9, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 9, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 9, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 9, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 9, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 9, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 9, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 4, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 4, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 4, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 4, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 4, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 6, 5, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 7, 5, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 7, 5, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 8, 5, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 8, 5, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 4, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 5, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 6, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 6, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 7, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 7, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 7, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 7, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 7, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 7, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 8, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 8, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 8, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 8, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 8, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 4, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 4, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 5, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 5, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 6, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 6, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 6, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 6, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 6, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 6, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 6, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 7, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 7, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 7, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 7, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 8, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 8, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 8, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 8, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 8, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 8, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 4, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 4, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 5, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 5, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 6, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 6, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 6, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 6, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 6, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 6, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 6, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 7, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 7, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 7, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 7, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 7, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 7, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 7, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 8, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 8, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 8, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 8, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 8, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 8, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 4, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 4, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 5, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 5, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 6, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 6, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 6, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 6, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 6, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 6, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 6, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 7, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 7, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 7, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 7, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 7, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 7, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 7, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 8, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 8, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 8, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 8, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 8, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 8, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 4, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 4, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 4, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 4, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 4, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 4, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 4, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 4, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 4, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 4, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 4, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 5, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 6, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 6, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 7, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 7, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 7, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 7, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 7, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 7, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 8, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 8, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 8, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 8, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 8, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 4, 6, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 5, 6, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 5, 6, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 7, 6, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 7, 6, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 7, 6, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 7, 6, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 7, 6, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 6, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 8, 6, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 8, 6, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 8, 6, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 6, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 6, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 6, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 4, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 4, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 4, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 4, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 5, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 5, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 5, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 5, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 5, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 5, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 5, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 6, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 6, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 6, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 6, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 6, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 7, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 7, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 7, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 7, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 7, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 7, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 8, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 8, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 8, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 8, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 8, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 8, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 8, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 8, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 4, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 4, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 4, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 4, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 5, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 5, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 5, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 5, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 5, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 5, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 5, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 6, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 6, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 6, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 6, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 6, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 7, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 7, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 7, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 7, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 7, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 7, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 7, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 7, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 7, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 8, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 8, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 8, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 8, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 8, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 8, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 8, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 8, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 4, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 4, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 4, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 4, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 5, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 5, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 5, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 5, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 5, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 5, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 5, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 6, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 6, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 6, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 6, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 6, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 7, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 7, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 7, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 7, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 7, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 7, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 7, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 7, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 7, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 8, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 8, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 8, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 8, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 8, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 8, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 8, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 9, 6, 3, 2, 1, 0] + sm_links: *id063 diff --git a/tests/generators/fork_choice_generated/block_tree_tree_2.yaml b/tests/generators/fork_choice_generated/block_tree_tree_2.yaml new file mode 100644 index 0000000000..8a5deccc6d --- /dev/null +++ b/tests/generators/fork_choice_generated/block_tree_tree_2.yaml @@ -0,0 +1,8424 @@ +- block_parents: &id002 [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1, 0] + sm_links: &id001 + - [0, 1] + - [0, 2] + - [0, 3] + - [0, 4] +- block_parents: &id003 [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 5, 4, 3, 2, 1, 0] + sm_links: *id001 +- block_parents: &id005 [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 5, 4, 3, 2, 1, 0] + sm_links: *id001 +- block_parents: &id006 [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 5, 4, 3, 2, 1, 0] + sm_links: *id001 +- block_parents: &id007 [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 5, 4, 3, 2, 1, 0] + sm_links: *id001 +- block_parents: *id002 + sm_links: &id004 + - [0, 1] + - [0, 2] + - [0, 3] + - [0, 5] +- block_parents: *id003 + sm_links: *id004 +- block_parents: *id005 + sm_links: *id004 +- block_parents: *id006 + sm_links: *id004 +- block_parents: *id007 + sm_links: *id004 +- block_parents: *id002 + sm_links: &id008 + - [0, 1] + - [0, 2] + - [0, 3] + - [1, 4] +- block_parents: *id003 + sm_links: *id008 +- block_parents: *id005 + sm_links: *id008 +- block_parents: *id006 + sm_links: *id008 +- block_parents: *id007 + sm_links: *id008 +- block_parents: *id002 + sm_links: &id009 + - [0, 1] + - [0, 2] + - [0, 3] + - [2, 4] +- block_parents: *id003 + sm_links: *id009 +- block_parents: *id005 + sm_links: *id009 +- block_parents: *id006 + sm_links: *id009 +- block_parents: *id007 + sm_links: *id009 +- block_parents: *id002 + sm_links: &id010 + - [0, 1] + - [0, 2] + - [0, 3] + - [3, 4] +- block_parents: *id003 + sm_links: *id010 +- block_parents: *id005 + sm_links: *id010 +- block_parents: *id006 + sm_links: *id010 +- block_parents: *id007 + sm_links: *id010 +- block_parents: *id002 + sm_links: &id011 + - [0, 1] + - [0, 2] + - [0, 3] + - [1, 5] +- block_parents: *id003 + sm_links: *id011 +- block_parents: *id005 + sm_links: *id011 +- block_parents: *id006 + sm_links: *id011 +- block_parents: *id007 + sm_links: *id011 +- block_parents: *id002 + sm_links: &id012 + - [0, 1] + - [0, 2] + - [0, 3] + - [2, 5] +- block_parents: *id003 + sm_links: *id012 +- block_parents: *id005 + sm_links: *id012 +- block_parents: *id006 + sm_links: *id012 +- block_parents: *id007 + sm_links: *id012 +- block_parents: *id002 + sm_links: &id013 + - [0, 1] + - [0, 2] + - [0, 3] + - [3, 5] +- block_parents: *id003 + sm_links: *id013 +- block_parents: *id005 + sm_links: *id013 +- block_parents: *id006 + sm_links: *id013 +- block_parents: *id007 + sm_links: *id013 +- block_parents: *id002 + sm_links: &id014 + - [0, 1] + - [0, 2] + - [1, 3] + - [1, 4] +- block_parents: *id003 + sm_links: *id014 +- block_parents: *id005 + sm_links: *id014 +- block_parents: *id006 + sm_links: *id014 +- block_parents: *id007 + sm_links: *id014 +- block_parents: *id002 + sm_links: &id015 + - [0, 1] + - [0, 2] + - [1, 3] + - [2, 4] +- block_parents: *id003 + sm_links: *id015 +- block_parents: *id005 + sm_links: *id015 +- block_parents: *id006 + sm_links: *id015 +- block_parents: *id007 + sm_links: *id015 +- block_parents: *id002 + sm_links: &id016 + - [0, 1] + - [0, 2] + - [1, 3] + - [3, 4] +- block_parents: *id003 + sm_links: *id016 +- block_parents: *id005 + sm_links: *id016 +- block_parents: *id006 + sm_links: *id016 +- block_parents: *id007 + sm_links: *id016 +- block_parents: *id002 + sm_links: &id017 + - [0, 1] + - [0, 2] + - [1, 3] + - [1, 5] +- block_parents: *id003 + sm_links: *id017 +- block_parents: *id005 + sm_links: *id017 +- block_parents: *id006 + sm_links: *id017 +- block_parents: *id007 + sm_links: *id017 +- block_parents: *id002 + sm_links: &id018 + - [0, 1] + - [0, 2] + - [1, 3] + - [2, 5] +- block_parents: *id003 + sm_links: *id018 +- block_parents: *id005 + sm_links: *id018 +- block_parents: *id006 + sm_links: *id018 +- block_parents: *id007 + sm_links: *id018 +- block_parents: *id002 + sm_links: &id019 + - [0, 1] + - [0, 2] + - [1, 3] + - [3, 5] +- block_parents: *id003 + sm_links: *id019 +- block_parents: *id005 + sm_links: *id019 +- block_parents: *id006 + sm_links: *id019 +- block_parents: *id007 + sm_links: *id019 +- block_parents: *id002 + sm_links: &id020 + - [0, 1] + - [0, 2] + - [2, 3] + - [2, 4] +- block_parents: *id003 + sm_links: *id020 +- block_parents: *id005 + sm_links: *id020 +- block_parents: *id006 + sm_links: *id020 +- block_parents: *id007 + sm_links: *id020 +- block_parents: *id002 + sm_links: &id021 + - [0, 1] + - [0, 2] + - [2, 3] + - [3, 4] +- block_parents: *id003 + sm_links: *id021 +- block_parents: *id005 + sm_links: *id021 +- block_parents: *id006 + sm_links: *id021 +- block_parents: *id007 + sm_links: *id021 +- block_parents: *id002 + sm_links: &id022 + - [0, 1] + - [0, 2] + - [2, 3] + - [2, 5] +- block_parents: *id003 + sm_links: *id022 +- block_parents: *id005 + sm_links: *id022 +- block_parents: *id006 + sm_links: *id022 +- block_parents: *id007 + sm_links: *id022 +- block_parents: *id002 + sm_links: &id023 + - [0, 1] + - [0, 2] + - [2, 3] + - [3, 5] +- block_parents: *id003 + sm_links: *id023 +- block_parents: *id005 + sm_links: *id023 +- block_parents: *id006 + sm_links: *id023 +- block_parents: *id007 + sm_links: *id023 +- block_parents: *id002 + sm_links: &id024 + - [0, 1] + - [0, 2] + - [0, 4] + - [0, 5] +- block_parents: *id003 + sm_links: *id024 +- block_parents: *id005 + sm_links: *id024 +- block_parents: *id006 + sm_links: *id024 +- block_parents: *id007 + sm_links: *id024 +- block_parents: *id002 + sm_links: &id025 + - [0, 1] + - [0, 2] + - [0, 4] + - [1, 5] +- block_parents: *id003 + sm_links: *id025 +- block_parents: *id005 + sm_links: *id025 +- block_parents: *id006 + sm_links: *id025 +- block_parents: *id007 + sm_links: *id025 +- block_parents: *id002 + sm_links: &id026 + - [0, 1] + - [0, 2] + - [0, 4] + - [2, 5] +- block_parents: *id003 + sm_links: *id026 +- block_parents: *id005 + sm_links: *id026 +- block_parents: *id006 + sm_links: *id026 +- block_parents: *id007 + sm_links: *id026 +- block_parents: *id002 + sm_links: &id027 + - [0, 1] + - [0, 2] + - [0, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id027 +- block_parents: *id005 + sm_links: *id027 +- block_parents: *id006 + sm_links: *id027 +- block_parents: *id007 + sm_links: *id027 +- block_parents: *id002 + sm_links: &id028 + - [0, 1] + - [0, 2] + - [1, 4] + - [1, 5] +- block_parents: *id003 + sm_links: *id028 +- block_parents: *id005 + sm_links: *id028 +- block_parents: *id006 + sm_links: *id028 +- block_parents: *id007 + sm_links: *id028 +- block_parents: *id002 + sm_links: &id029 + - [0, 1] + - [0, 2] + - [1, 4] + - [2, 5] +- block_parents: *id003 + sm_links: *id029 +- block_parents: *id005 + sm_links: *id029 +- block_parents: *id006 + sm_links: *id029 +- block_parents: *id007 + sm_links: *id029 +- block_parents: *id002 + sm_links: &id030 + - [0, 1] + - [0, 2] + - [1, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id030 +- block_parents: *id005 + sm_links: *id030 +- block_parents: *id006 + sm_links: *id030 +- block_parents: *id007 + sm_links: *id030 +- block_parents: *id002 + sm_links: &id031 + - [0, 1] + - [0, 2] + - [2, 4] + - [2, 5] +- block_parents: *id003 + sm_links: *id031 +- block_parents: *id005 + sm_links: *id031 +- block_parents: *id006 + sm_links: *id031 +- block_parents: *id007 + sm_links: *id031 +- block_parents: *id002 + sm_links: &id032 + - [0, 1] + - [0, 2] + - [2, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id032 +- block_parents: *id005 + sm_links: *id032 +- block_parents: *id006 + sm_links: *id032 +- block_parents: *id007 + sm_links: *id032 +- block_parents: *id002 + sm_links: &id033 + - [0, 1] + - [0, 3] + - [0, 4] + - [0, 5] +- block_parents: *id003 + sm_links: *id033 +- block_parents: *id005 + sm_links: *id033 +- block_parents: *id006 + sm_links: *id033 +- block_parents: *id007 + sm_links: *id033 +- block_parents: *id002 + sm_links: &id034 + - [0, 2] + - [0, 3] + - [0, 4] + - [0, 5] +- block_parents: *id003 + sm_links: *id034 +- block_parents: *id005 + sm_links: *id034 +- block_parents: *id006 + sm_links: *id034 +- block_parents: *id007 + sm_links: *id034 +- block_parents: *id002 + sm_links: &id035 + - [0, 1] + - [0, 3] + - [0, 4] + - [1, 5] +- block_parents: *id003 + sm_links: *id035 +- block_parents: *id005 + sm_links: *id035 +- block_parents: *id006 + sm_links: *id035 +- block_parents: *id007 + sm_links: *id035 +- block_parents: *id002 + sm_links: &id036 + - [0, 1] + - [0, 3] + - [0, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id036 +- block_parents: *id005 + sm_links: *id036 +- block_parents: *id006 + sm_links: *id036 +- block_parents: *id007 + sm_links: *id036 +- block_parents: *id002 + sm_links: &id037 + - [0, 1] + - [0, 3] + - [0, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id037 +- block_parents: *id005 + sm_links: *id037 +- block_parents: *id006 + sm_links: *id037 +- block_parents: *id007 + sm_links: *id037 +- block_parents: *id002 + sm_links: &id038 + - [0, 2] + - [0, 3] + - [0, 4] + - [2, 5] +- block_parents: *id003 + sm_links: *id038 +- block_parents: *id005 + sm_links: *id038 +- block_parents: *id006 + sm_links: *id038 +- block_parents: *id007 + sm_links: *id038 +- block_parents: *id002 + sm_links: &id039 + - [0, 2] + - [0, 3] + - [0, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id039 +- block_parents: *id005 + sm_links: *id039 +- block_parents: *id006 + sm_links: *id039 +- block_parents: *id007 + sm_links: *id039 +- block_parents: *id002 + sm_links: &id040 + - [0, 2] + - [0, 3] + - [0, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id040 +- block_parents: *id005 + sm_links: *id040 +- block_parents: *id006 + sm_links: *id040 +- block_parents: *id007 + sm_links: *id040 +- block_parents: *id002 + sm_links: &id041 + - [0, 1] + - [0, 3] + - [1, 4] + - [1, 5] +- block_parents: *id003 + sm_links: *id041 +- block_parents: *id005 + sm_links: *id041 +- block_parents: *id006 + sm_links: *id041 +- block_parents: *id007 + sm_links: *id041 +- block_parents: *id002 + sm_links: &id042 + - [0, 1] + - [0, 3] + - [1, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id042 +- block_parents: *id005 + sm_links: *id042 +- block_parents: *id006 + sm_links: *id042 +- block_parents: *id007 + sm_links: *id042 +- block_parents: *id002 + sm_links: &id043 + - [0, 1] + - [0, 3] + - [1, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id043 +- block_parents: *id005 + sm_links: *id043 +- block_parents: *id006 + sm_links: *id043 +- block_parents: *id007 + sm_links: *id043 +- block_parents: *id002 + sm_links: &id044 + - [0, 1] + - [0, 3] + - [3, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id044 +- block_parents: *id005 + sm_links: *id044 +- block_parents: *id006 + sm_links: *id044 +- block_parents: *id007 + sm_links: *id044 +- block_parents: *id002 + sm_links: &id045 + - [0, 1] + - [0, 3] + - [3, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id045 +- block_parents: *id005 + sm_links: *id045 +- block_parents: *id006 + sm_links: *id045 +- block_parents: *id007 + sm_links: *id045 +- block_parents: *id002 + sm_links: &id046 + - [0, 2] + - [0, 3] + - [2, 4] + - [2, 5] +- block_parents: *id003 + sm_links: *id046 +- block_parents: *id005 + sm_links: *id046 +- block_parents: *id006 + sm_links: *id046 +- block_parents: *id007 + sm_links: *id046 +- block_parents: *id002 + sm_links: &id047 + - [0, 2] + - [0, 3] + - [2, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id047 +- block_parents: *id005 + sm_links: *id047 +- block_parents: *id006 + sm_links: *id047 +- block_parents: *id007 + sm_links: *id047 +- block_parents: *id002 + sm_links: &id048 + - [0, 2] + - [0, 3] + - [2, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id048 +- block_parents: *id005 + sm_links: *id048 +- block_parents: *id006 + sm_links: *id048 +- block_parents: *id007 + sm_links: *id048 +- block_parents: *id002 + sm_links: &id049 + - [0, 2] + - [0, 3] + - [3, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id049 +- block_parents: *id005 + sm_links: *id049 +- block_parents: *id006 + sm_links: *id049 +- block_parents: *id007 + sm_links: *id049 +- block_parents: *id002 + sm_links: &id050 + - [0, 2] + - [0, 3] + - [3, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id050 +- block_parents: *id005 + sm_links: *id050 +- block_parents: *id006 + sm_links: *id050 +- block_parents: *id007 + sm_links: *id050 +- block_parents: *id002 + sm_links: &id051 + - [0, 1] + - [1, 3] + - [1, 4] + - [1, 5] +- block_parents: *id003 + sm_links: *id051 +- block_parents: *id005 + sm_links: *id051 +- block_parents: *id006 + sm_links: *id051 +- block_parents: *id007 + sm_links: *id051 +- block_parents: *id002 + sm_links: &id052 + - [0, 1] + - [1, 3] + - [1, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id052 +- block_parents: *id005 + sm_links: *id052 +- block_parents: *id006 + sm_links: *id052 +- block_parents: *id007 + sm_links: *id052 +- block_parents: *id002 + sm_links: &id053 + - [0, 1] + - [1, 3] + - [1, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id053 +- block_parents: *id005 + sm_links: *id053 +- block_parents: *id006 + sm_links: *id053 +- block_parents: *id007 + sm_links: *id053 +- block_parents: *id002 + sm_links: &id054 + - [0, 1] + - [1, 3] + - [3, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id054 +- block_parents: *id005 + sm_links: *id054 +- block_parents: *id006 + sm_links: *id054 +- block_parents: *id007 + sm_links: *id054 +- block_parents: *id002 + sm_links: &id055 + - [0, 1] + - [1, 3] + - [3, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id055 +- block_parents: *id005 + sm_links: *id055 +- block_parents: *id006 + sm_links: *id055 +- block_parents: *id007 + sm_links: *id055 +- block_parents: *id002 + sm_links: &id056 + - [0, 2] + - [2, 3] + - [2, 4] + - [2, 5] +- block_parents: *id003 + sm_links: *id056 +- block_parents: *id005 + sm_links: *id056 +- block_parents: *id006 + sm_links: *id056 +- block_parents: *id007 + sm_links: *id056 +- block_parents: *id002 + sm_links: &id057 + - [0, 2] + - [2, 3] + - [2, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id057 +- block_parents: *id005 + sm_links: *id057 +- block_parents: *id006 + sm_links: *id057 +- block_parents: *id007 + sm_links: *id057 +- block_parents: *id002 + sm_links: &id058 + - [0, 2] + - [2, 3] + - [2, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id058 +- block_parents: *id005 + sm_links: *id058 +- block_parents: *id006 + sm_links: *id058 +- block_parents: *id007 + sm_links: *id058 +- block_parents: *id002 + sm_links: &id059 + - [0, 2] + - [2, 3] + - [3, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id059 +- block_parents: *id005 + sm_links: *id059 +- block_parents: *id006 + sm_links: *id059 +- block_parents: *id007 + sm_links: *id059 +- block_parents: *id002 + sm_links: &id060 + - [0, 2] + - [2, 3] + - [3, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id060 +- block_parents: *id005 + sm_links: *id060 +- block_parents: *id006 + sm_links: *id060 +- block_parents: *id007 + sm_links: *id060 +- block_parents: [0, 0, 1, 0, 0, 0] + sm_links: &id061 + - [0, 1] + - [0, 2] + - [2, 3] + - [3, 4] +- block_parents: [0, 0, 0, 1, 0, 0] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 0, 0] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 0, 0] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 0, 0] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 1, 0] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 1, 0] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 1, 0] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 1, 0] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 1, 0] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 1, 0] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 2, 0] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 2, 0] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 2, 0] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 2, 0] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 2, 0] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 2, 0] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 3, 0] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 3, 0] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 3, 0] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 3, 0] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 3, 0] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 3, 0] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 0, 1] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 0, 1] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 0, 1] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 0, 1] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 0, 1] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 0, 1] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 1, 1] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 1, 1] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 1, 1] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 1, 1] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 1, 1] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 1, 1] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 2, 1] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 2, 1] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 2, 1] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 2, 1] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 2, 1] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 2, 1] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 3, 1] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 3, 1] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 3, 1] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 3, 1] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 3, 1] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 3, 1] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 0, 2] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 0, 2] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 0, 2] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 0, 2] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 0, 2] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 0, 2] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 1, 2] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 1, 2] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 1, 2] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 1, 2] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 1, 2] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 1, 2] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 2, 2] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 2, 2] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 2, 2] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 2, 2] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 2, 2] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 2, 2] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 3, 2] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 3, 2] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 3, 2] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 3, 2] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 3, 2] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 3, 2] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 0, 3] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 0, 3] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 0, 3] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 0, 3] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 0, 3] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 0, 3] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 1, 3] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 1, 3] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 1, 3] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 1, 3] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 1, 3] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 1, 3] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 2, 3] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 2, 3] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 2, 3] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 2, 3] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 2, 3] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 2, 3] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 3, 3] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 3, 3] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 3, 3] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 3, 3] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 3, 3] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 3, 3] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 0, 4] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 0, 4] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 0, 4] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 0, 4] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 0, 4] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 0, 4] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 1, 4] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 1, 4] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 1, 4] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 1, 4] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 1, 4] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 1, 4] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 2, 4] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 2, 4] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 2, 4] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 2, 4] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 2, 4] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 2, 4] + sm_links: *id061 +- block_parents: [0, 0, 0, 0, 3, 4] + sm_links: *id061 +- block_parents: [0, 0, 1, 0, 3, 4] + sm_links: *id061 +- block_parents: [0, 0, 0, 1, 3, 4] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 3, 4] + sm_links: *id061 +- block_parents: [0, 0, 0, 2, 3, 4] + sm_links: *id061 +- block_parents: [0, 0, 1, 2, 3, 4] + sm_links: *id061 +- block_parents: [0, 0, 1, 1, 1, 0, 0] + sm_links: &id062 + - [0, 1] + - [0, 2] + - [2, 3] + - [3, 4] +- block_parents: [0, 0, 1, 2, 1, 0, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 0, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 0, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 0, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 0, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 1, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 1, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 1, 0] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 1, 0] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 1, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 1, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 1, 0] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 1, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 1, 0] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 1, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 1, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 1, 0] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 1, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 1, 0] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 1, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 1, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 2, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 2, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 2, 0] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 2, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 1, 2, 0] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 2, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 2, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 2, 0] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 2, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 2, 0] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 2, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 2, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 2, 0] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 2, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 2, 0] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 2, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 2, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 3, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 3, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 3, 0] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 3, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 1, 3, 0] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 3, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 3, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 3, 0] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 3, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 3, 0] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 3, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 3, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 3, 0] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 3, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 3, 0] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 3, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 3, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 4, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 4, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 4, 0] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 4, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 1, 4, 0] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 4, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 4, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 4, 0] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 4, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 4, 0] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 4, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 4, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 4, 0] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 4, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 4, 0] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 4, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 4, 0] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 0, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 0, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 0, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 0, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 0, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 0, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 0, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 0, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 0, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 0, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 0, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 0, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 0, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 0, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 0, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 0, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 0, 1, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 0, 1, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 0, 1, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 1, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 1, 1, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 1, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 2, 1, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 1, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 1, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 1, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 1, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 3, 1, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 1, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 1, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 1, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 1, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 0, 2, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 0, 2, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 2, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 0, 2, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 2, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 1, 2, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 2, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 2, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 2, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 2, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 2, 2, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 2, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 2, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 2, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 2, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 2, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 3, 2, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 2, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 2, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 2, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 2, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 2, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 0, 3, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 0, 3, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 3, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 0, 3, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 3, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 1, 3, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 3, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 3, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 3, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 3, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 2, 3, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 3, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 3, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 3, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 3, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 3, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 3, 3, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 3, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 3, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 3, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 3, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 3, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 0, 4, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 0, 4, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 4, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 0, 4, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 4, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 1, 4, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 4, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 4, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 4, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 4, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 2, 4, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 4, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 4, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 4, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 4, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 4, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 3, 4, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 4, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 4, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 4, 1] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 4, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 4, 1] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 0, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 0, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 0, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 0, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 1, 0, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 0, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 0, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 0, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 0, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 0, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 0, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 0, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 0, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 0, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 0, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 0, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 0, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 0, 1, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 0, 1, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 1, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 0, 1, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 1, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 1, 1, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 1, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 1, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 1, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 1, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 2, 1, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 1, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 1, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 1, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 1, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 1, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 3, 1, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 1, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 1, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 1, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 1, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 1, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 0, 2, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 0, 2, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 2, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 0, 2, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 2, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 1, 2, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 2, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 2, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 1, 2, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 2, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 2, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 2, 2, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 2, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 2, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 2, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 3, 2, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 2, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 2, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 2, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 2, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 2, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 0, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 0, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 0, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 1, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 1, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 2, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 3, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 3, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 0, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 0, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 0, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 1, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 1, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 2, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 3, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 4, 2] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 0, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 0, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 0, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 0, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 1, 0, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 0, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 0, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 0, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 0, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 0, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 0, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 0, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 0, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 0, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 0, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 0, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 0, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 0, 1, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 0, 1, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 1, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 0, 1, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 1, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 1, 1, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 1, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 1, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 1, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 1, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 2, 1, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 1, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 1, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 1, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 1, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 1, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 3, 1, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 1, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 1, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 1, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 1, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 1, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 0, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 0, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 0, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 1, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 1, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 2, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 3, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 2, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 0, 3, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 0, 3, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 3, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 0, 3, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 3, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 1, 3, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 3, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 3, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 1, 3, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 3, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 3, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 2, 3, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 3, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 3, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 3, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 3, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 3, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 3, 3, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 3, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 3, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 3, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 3, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 3, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 0, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 0, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 0, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 1, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 1, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 2, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 3, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 4, 3] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 0, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 0, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 0, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 0, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 1, 0, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 0, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 0, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 0, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 0, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 0, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 0, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 0, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 0, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 0, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 0, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 0, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 0, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 0, 1, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 0, 1, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 1, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 0, 1, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 1, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 1, 1, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 1, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 1, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 1, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 1, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 2, 1, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 1, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 1, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 1, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 1, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 1, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 3, 1, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 1, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 1, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 1, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 1, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 1, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 0, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 0, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 0, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 1, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 1, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 2, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 3, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 2, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 0, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 0, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 0, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 1, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 1, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 2, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 3, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 3, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 0, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 0, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 0, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 1, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 1, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 2, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 3, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 4, 4] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 0, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 0, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 0, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 0, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 1, 0, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 0, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 0, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 0, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 0, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 0, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 0, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 0, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 0, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 0, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 0, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 0, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 0, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 0, 1, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 0, 1, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 1, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 0, 1, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 1, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 1, 1, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 1, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 1, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 1, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 1, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 2, 1, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 1, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 1, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 1, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 1, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 1, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 3, 1, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 1, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 1, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 1, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 1, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 1, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 0, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 0, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 0, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 1, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 1, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 2, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 3, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 2, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 0, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 0, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 0, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 1, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 1, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 2, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 3, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 3, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 0, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 0, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 0, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 0, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 0, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 1, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 1, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 1, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 1, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 1, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 1, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 2, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 2, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 2, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 2, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 2, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 2, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 0, 3, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 0, 3, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 1, 3, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 1, 3, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 0, 2, 3, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 4, 5] + sm_links: *id062 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1, 0] + sm_links: &id063 + - [0, 1] + - [0, 2] + - [2, 3] + - [3, 4] +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 5, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 5, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 5, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 5, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 6, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 6, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 6, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 6, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 6, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 6, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 6, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 6, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 6, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 5, 7, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 7, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 6, 7, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 7, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 7, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 7, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 7, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 7, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 7, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 7, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 7, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 7, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 7, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 7, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 5, 8, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 8, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 6, 8, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 8, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 8, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 8, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 8, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 8, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 8, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 8, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 8, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 8, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 8, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 5, 9, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 9, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 6, 9, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 9, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 9, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 9, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 9, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 9, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 9, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 9, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 9, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 9, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 9, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 9, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 9, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 4, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 4, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 4, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 4, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 4, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 4, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 6, 5, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 7, 5, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 7, 5, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 8, 5, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 8, 5, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 4, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 5, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 6, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 6, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 7, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 7, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 7, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 7, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 7, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 7, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 8, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 8, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 8, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 8, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 8, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 6, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 4, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 4, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 5, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 5, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 6, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 6, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 6, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 6, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 6, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 6, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 6, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 7, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 7, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 7, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 7, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 8, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 8, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 8, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 8, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 8, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 8, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 7, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 4, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 4, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 5, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 5, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 6, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 6, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 6, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 6, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 6, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 6, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 6, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 7, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 7, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 7, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 7, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 7, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 7, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 7, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 8, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 8, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 8, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 8, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 8, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 8, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 4, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 4, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 5, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 5, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 6, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 6, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 6, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 6, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 6, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 6, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 6, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 7, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 7, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 7, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 7, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 7, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 7, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 7, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 8, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 8, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 8, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 8, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 8, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 8, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 5, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 4, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 4, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 4, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 4, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 4, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 4, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 4, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 4, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 4, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 4, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 4, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 5, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 6, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 6, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 7, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 7, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 7, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 7, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 7, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 7, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 8, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 8, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 8, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 8, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 8, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 5, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 4, 6, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 5, 6, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 5, 6, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 7, 6, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 7, 6, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 7, 6, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 7, 6, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 7, 6, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 6, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 8, 6, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 8, 6, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 8, 6, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 6, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 6, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 6, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 4, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 4, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 4, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 4, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 5, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 5, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 5, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 5, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 5, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 5, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 5, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 6, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 6, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 6, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 6, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 6, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 7, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 7, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 7, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 7, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 7, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 7, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 8, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 8, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 8, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 8, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 8, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 8, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 8, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 8, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 7, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 4, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 4, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 4, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 4, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 5, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 5, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 5, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 5, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 5, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 5, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 5, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 6, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 6, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 6, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 6, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 6, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 7, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 7, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 7, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 7, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 7, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 7, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 7, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 7, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 7, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 8, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 8, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 8, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 8, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 8, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 8, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 8, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 8, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 4, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 4, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 4, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 4, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 5, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 5, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 5, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 5, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 5, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 5, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 5, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 6, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 6, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 6, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 6, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 6, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 7, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 7, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 7, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 7, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 7, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 7, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 7, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 7, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 7, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 8, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 8, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 8, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 8, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 8, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 8, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 8, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 8, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 6, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 5, 4, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 4, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 6, 4, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 4, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 4, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 4, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 4, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 4, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 4, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 4, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 4, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 4, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 4, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 4, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 4, 5, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 4, 5, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 5, 5, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 5, 5, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 6, 5, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 6, 5, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 6, 5, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 6, 5, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 6, 5, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 6, 5, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 6, 5, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 5, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 7, 5, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 7, 5, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 7, 5, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 5, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 7, 5, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 5, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 8, 5, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 8, 5, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 8, 5, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 8, 5, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 8, 5, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 5, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 5, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 5, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 8, 5, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 5, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 4, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 4, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 4, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 4, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 5, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 5, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 5, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 5, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 5, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 5, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 5, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 6, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 6, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 6, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 6, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 7, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 7, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 7, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 7, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 7, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 7, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 8, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 8, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 8, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 8, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 8, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 8, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 8, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 8, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 6, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 4, 7, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 4, 7, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 4, 7, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 5, 7, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 5, 7, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 5, 7, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 5, 7, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 5, 7, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 5, 7, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 6, 7, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 6, 7, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 6, 7, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 6, 7, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 6, 7, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 6, 7, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 6, 7, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 6, 7, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 7, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 8, 7, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 8, 7, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 8, 7, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 8, 7, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 8, 7, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 7, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 8, 7, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 8, 7, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 7, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 8, 7, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 7, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 4, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 4, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 4, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 4, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 4, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 5, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 5, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 5, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 5, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 5, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 5, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 5, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 5, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 5, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 6, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 6, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 6, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 6, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 6, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 6, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 6, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 6, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 6, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 6, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 6, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 7, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 7, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 7, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 7, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 7, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 7, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 7, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 8, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 8, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 8, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 8, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 8, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 8, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 8, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 8, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 8, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 4, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 4, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 4, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 4, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 4, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 5, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 5, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 5, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 5, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 5, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 5, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 5, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 5, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 5, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 6, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 6, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 6, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 6, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 6, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 6, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 6, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 6, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 6, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 6, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 6, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 7, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 7, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 7, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 7, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 7, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 7, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 7, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 8, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 8, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 8, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 8, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 8, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 8, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 8, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 8, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 8, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 7, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 5, 4, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 4, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 6, 4, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 4, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 4, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 4, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 4, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 4, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 4, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 4, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 4, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 4, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 4, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 4, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 4, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 4, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 4, 5, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 4, 5, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 5, 5, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 5, 5, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 6, 5, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 6, 5, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 6, 5, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 6, 5, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 6, 5, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 6, 5, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 6, 5, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 5, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 7, 5, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 7, 5, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 7, 5, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 5, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 7, 5, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 5, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 7, 5, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 7, 5, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 7, 5, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 5, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 8, 5, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 8, 5, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 8, 5, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 8, 5, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 8, 5, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 5, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 5, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 5, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 8, 5, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 5, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 4, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 4, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 4, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 4, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 5, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 5, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 5, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 5, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 5, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 5, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 5, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 6, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 6, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 6, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 6, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 7, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 7, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 7, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 7, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 7, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 7, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 7, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 7, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 7, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 8, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 8, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 8, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 8, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 8, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 8, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 8, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 8, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 6, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 4, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 4, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 4, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 4, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 4, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 5, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 5, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 5, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 5, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 5, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 5, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 5, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 5, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 5, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 6, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 6, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 6, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 6, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 6, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 6, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 6, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 6, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 6, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 6, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 7, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 7, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 7, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 7, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 7, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 7, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 7, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 8, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 8, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 8, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 8, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 8, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 8, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 8, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 8, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 8, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 7, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 4, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 4, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 4, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 4, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 4, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 5, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 5, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 5, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 5, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 5, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 5, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 5, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 5, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 5, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 6, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 6, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 6, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 6, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 6, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 6, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 6, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 6, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 6, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 6, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 6, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 7, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 7, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 7, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 7, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 7, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 7, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 7, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 7, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 7, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 7, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 4, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 4, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 4, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 4, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 4, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 5, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 5, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 5, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 5, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 5, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 5, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 5, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 5, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 5, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 6, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 6, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 6, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 6, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 6, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 6, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 6, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 6, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 6, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 6, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 6, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 7, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 7, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 7, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 7, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 7, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 7, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 7, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 7, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 7, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 7, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 8, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 8, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 8, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 8, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 8, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 8, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 8, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 8, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 8, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 5, 4, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 4, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 6, 4, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 4, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 4, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 4, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 4, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 4, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 4, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 4, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 4, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 4, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 4, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 4, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 4, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 4, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 4, 5, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 4, 5, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 5, 5, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 5, 5, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 6, 5, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 6, 5, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 6, 5, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 6, 5, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 6, 5, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 6, 5, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 6, 5, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 5, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 7, 5, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 7, 5, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 7, 5, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 5, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 7, 5, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 5, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 7, 5, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 7, 5, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 7, 5, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 5, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 8, 5, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 8, 5, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 8, 5, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 8, 5, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 8, 5, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 5, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 5, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 5, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 8, 5, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 5, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 4, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 4, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 4, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 4, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 5, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 5, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 5, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 5, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 5, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 5, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 5, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 6, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 6, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 6, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 6, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 7, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 7, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 7, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 7, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 7, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 7, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 7, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 7, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 7, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 8, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 8, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 8, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 8, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 8, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 8, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 8, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 8, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 6, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 4, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 4, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 4, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 4, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 4, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 5, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 5, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 5, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 5, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 5, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 5, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 5, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 5, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 5, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 6, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 6, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 6, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 6, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 6, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 6, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 6, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 6, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 6, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 6, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 6, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 7, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 7, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 7, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 7, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 7, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 7, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 7, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 8, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 8, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 8, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 8, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 8, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 8, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 8, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 8, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 8, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 7, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 4, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 4, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 4, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 4, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 4, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 5, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 5, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 5, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 5, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 5, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 5, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 5, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 5, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 5, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 6, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 6, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 6, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 6, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 6, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 6, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 6, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 6, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 6, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 6, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 6, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 7, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 7, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 7, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 7, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 7, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 7, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 7, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 7, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 7, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 7, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 8, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 8, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 8, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 8, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 8, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 8, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 8, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 8, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 8, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 4, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 4, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 4, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 4, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 4, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 5, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 5, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 5, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 5, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 5, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 5, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 5, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 5, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 5, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 6, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 6, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 6, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 6, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 6, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 6, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 6, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 6, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 6, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 6, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 6, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 7, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 7, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 7, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 7, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 7, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 7, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 7, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 7, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 7, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 7, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 8, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 8, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 8, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 8, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 8, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 8, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 8, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 8, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 8, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 5, 4, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 4, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 6, 4, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 4, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 4, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 4, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 4, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 4, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 4, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 4, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 4, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 4, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 4, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 4, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 4, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 4, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 4, 5, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 4, 5, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 5, 5, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 5, 5, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 6, 5, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 6, 5, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 6, 5, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 6, 5, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 6, 5, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 6, 5, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 6, 5, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 5, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 7, 5, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 7, 5, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 7, 5, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 5, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 7, 5, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 5, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 7, 5, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 7, 5, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 7, 5, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 5, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 8, 5, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 8, 5, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 8, 5, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 8, 5, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 8, 5, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 5, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 5, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 5, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 8, 5, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 5, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 4, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 4, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 4, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 4, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 5, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 5, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 5, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 5, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 5, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 5, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 5, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 6, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 6, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 6, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 6, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 7, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 7, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 7, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 7, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 7, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 7, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 7, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 7, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 7, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 8, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 8, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 8, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 8, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 8, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 8, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 8, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 8, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 6, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 4, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 4, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 4, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 4, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 4, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 5, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 5, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 5, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 5, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 5, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 5, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 5, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 5, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 5, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 6, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 6, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 6, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 6, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 6, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 6, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 6, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 6, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 6, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 6, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 6, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 7, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 7, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 7, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 7, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 7, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 7, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 7, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 8, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 8, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 8, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 8, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 8, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 8, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 8, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 8, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 8, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 7, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 4, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 4, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 4, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 4, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 4, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 5, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 5, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 5, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 5, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 5, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 5, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 5, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 5, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 5, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 6, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 6, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 6, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 6, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 6, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 6, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 6, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 6, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 6, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 6, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 6, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 7, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 7, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 7, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 7, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 7, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 7, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 7, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 7, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 7, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 7, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 8, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 8, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 8, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 8, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 8, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 8, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 8, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 8, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 8, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 4, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 4, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 4, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 4, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 4, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 5, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 5, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 5, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 5, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 5, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 5, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 5, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 5, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 5, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 6, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 6, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 6, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 6, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 6, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 6, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 6, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 6, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 6, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 6, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 6, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 7, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 7, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 7, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 7, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 7, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 7, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 7, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 7, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 7, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 7, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 8, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 8, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 8, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 8, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 8, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 8, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 8, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 8, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 8, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 8, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 3, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 5, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 5, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 5, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 5, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 5, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 6, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 6, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 6, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 6, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 6, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 6, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 6, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 6, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 6, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 5, 7, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 7, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 6, 7, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 7, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 7, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 7, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 7, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 7, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 7, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 7, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 7, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 7, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 7, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 7, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 5, 8, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 8, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 6, 8, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 8, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 8, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 8, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 8, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 8, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 8, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 8, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 8, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 8, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 8, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 5, 9, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 9, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 6, 9, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 9, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 9, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 9, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 9, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 9, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 9, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 9, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 9, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 9, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 9, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 9, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 9, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 3, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 6, 5, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 7, 5, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 7, 5, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 8, 5, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 8, 5, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 5, 6, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 6, 6, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 7, 6, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 7, 6, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 7, 6, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 7, 6, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 8, 6, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 8, 6, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 8, 6, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 8, 6, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 5, 7, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 5, 7, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 6, 7, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 6, 7, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 6, 7, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 6, 7, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 7, 7, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 7, 7, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 7, 7, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 8, 7, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 8, 7, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 8, 7, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 8, 7, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 8, 7, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 5, 8, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 5, 8, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 6, 8, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 6, 8, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 6, 8, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 6, 8, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 7, 8, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 7, 8, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 7, 8, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 7, 8, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 7, 8, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 8, 8, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 8, 8, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 8, 8, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 8, 8, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 8, 8, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 5, 9, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 5, 9, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 6, 9, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 6, 9, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 6, 9, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 6, 9, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 7, 9, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 7, 9, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 7, 9, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 7, 9, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 7, 9, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 8, 9, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 8, 9, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 8, 9, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 8, 9, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 8, 9, 4, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 3, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 3, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 3, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 3, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 3, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 6, 4, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 7, 4, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 7, 4, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 8, 4, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 8, 4, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 6, 5, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 6, 5, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 7, 5, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 7, 5, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 7, 5, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 7, 5, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 8, 5, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 8, 5, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 8, 5, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 8, 5, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 3, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 4, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 5, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 5, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 6, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 6, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 6, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 6, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 7, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 7, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 7, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 7, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 7, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 7, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 7, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 7, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 7, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 7, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 7, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 7, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 7, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 7, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 7, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 8, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 8, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 8, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 8, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 8, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 8, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 8, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 8, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 8, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 8, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 8, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 8, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 8, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 8, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 8, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 6, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 3, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 3, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 4, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 4, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 5, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 5, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 5, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 5, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 6, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 6, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 6, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 6, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 6, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 6, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 6, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 6, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 6, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 6, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 6, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 6, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 6, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 6, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 6, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 7, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 7, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 7, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 7, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 7, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 7, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 7, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 7, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 7, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 7, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 7, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 8, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 8, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 8, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 8, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 8, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 8, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 8, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 8, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 8, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 8, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 8, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 8, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 8, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 8, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 8, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 8, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 8, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 8, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 7, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 3, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 3, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 4, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 4, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 5, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 5, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 5, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 5, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 6, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 6, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 6, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 6, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 6, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 6, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 6, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 6, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 6, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 6, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 6, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 6, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 6, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 6, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 6, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 7, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 7, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 7, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 7, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 7, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 7, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 7, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 7, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 7, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 7, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 7, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 7, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 7, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 7, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 7, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 7, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 7, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 7, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 8, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 8, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 8, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 8, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 8, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 8, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 8, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 8, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 8, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 8, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 8, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 8, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 8, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 8, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 8, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 8, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 8, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 8, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 3, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 3, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 4, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 4, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 5, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 5, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 5, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 5, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 6, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 6, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 6, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 6, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 6, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 6, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 6, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 6, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 6, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 6, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 6, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 6, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 6, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 6, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 6, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 7, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 7, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 7, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 7, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 7, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 7, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 7, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 7, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 7, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 7, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 7, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 7, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 7, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 7, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 7, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 7, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 7, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 7, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 8, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 8, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 8, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 8, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 8, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 8, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 8, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 8, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 8, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 8, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 8, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 8, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 8, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 8, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 8, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 8, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 8, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 8, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 5, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 3, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 3, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 3, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 3, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 3, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 3, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 3, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 3, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 3, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 3, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 5, 4, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 6, 4, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 7, 4, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 7, 4, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 7, 4, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 7, 4, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 8, 4, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 8, 4, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 8, 4, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 8, 4, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 3, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 4, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 5, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 5, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 6, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 6, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 6, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 6, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 7, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 7, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 7, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 7, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 7, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 7, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 7, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 7, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 7, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 7, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 7, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 7, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 7, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 7, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 7, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 8, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 8, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 8, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 8, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 8, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 8, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 8, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 8, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 8, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 8, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 8, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 8, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 8, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 8, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 8, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 5, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 3, 6, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 4, 6, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 5, 6, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 5, 6, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 5, 6, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 5, 6, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 7, 6, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 7, 6, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 7, 6, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 7, 6, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 7, 6, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 7, 6, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 7, 6, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 7, 6, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 7, 6, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 7, 6, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 7, 6, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 6, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 8, 6, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 8, 6, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 8, 6, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 8, 6, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 8, 6, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 8, 6, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 8, 6, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 8, 6, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 8, 6, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 8, 6, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 8, 6, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 6, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 3, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 3, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 3, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 3, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 4, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 4, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 4, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 4, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 5, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 5, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 5, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 5, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 5, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 5, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 5, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 5, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 5, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 5, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 5, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 5, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 5, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 5, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 5, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 6, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 6, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 6, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 6, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 6, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 6, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 6, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 6, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 6, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 6, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 6, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 7, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 7, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 7, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 7, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 7, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 7, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 7, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 7, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 7, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 7, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 7, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 7, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 7, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 7, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 7, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 7, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 8, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 8, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 8, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 8, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 8, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 8, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 8, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 8, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 8, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 8, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 8, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 8, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 8, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 8, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 8, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 8, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 8, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 8, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 8, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 8, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 8, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 8, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 8, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 8, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 7, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 3, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 3, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 3, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 3, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 4, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 4, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 4, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 4, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 5, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 5, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 5, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 5, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 5, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 5, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 5, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 5, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 5, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 5, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 5, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 5, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 5, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 5, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 5, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 6, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 6, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 6, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 6, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 6, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 6, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 6, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 6, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 6, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 6, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 6, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 7, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 7, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 7, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 7, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 7, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 7, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 7, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 7, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 7, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 7, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 7, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 7, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 7, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 7, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 7, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 7, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 7, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 7, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 7, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 7, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 7, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 7, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 7, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 7, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 8, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 8, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 8, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 8, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 8, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 8, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 8, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 8, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 8, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 8, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 8, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 8, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 8, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 8, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 8, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 8, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 8, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 8, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 8, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 8, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 8, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 8, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 8, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 8, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 3, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 3, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 3, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 3, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 4, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 4, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 4, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 4, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 5, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 5, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 5, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 5, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 5, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 5, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 5, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 5, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 5, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 5, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 5, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 5, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 5, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 5, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 5, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 6, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 6, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 6, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 6, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 6, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 6, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 6, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 6, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 6, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 6, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 6, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 7, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 7, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 7, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 7, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 7, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 7, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 7, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 7, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 7, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 7, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 7, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 7, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 7, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 7, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 7, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 7, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 7, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 7, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 7, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 7, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 7, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 7, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 7, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 7, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 8, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 8, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 8, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 8, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 8, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 8, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 8, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 8, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 8, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 8, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 8, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 8, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 8, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 8, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 8, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 8, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 8, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 8, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 8, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 8, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 8, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 8, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 8, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 8, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 6, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 5, 3, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 3, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 6, 3, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 3, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 3, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 3, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 3, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 3, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 3, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 3, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 3, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 3, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 3, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 3, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 5, 4, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 5, 4, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 6, 4, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 6, 4, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 6, 4, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 6, 4, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 7, 4, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 7, 4, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 7, 4, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 8, 4, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 8, 4, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 8, 4, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 8, 4, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 8, 4, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 3, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 3, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 4, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 4, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 5, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 5, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 5, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 5, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 6, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 6, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 6, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 6, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 6, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 6, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 6, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 6, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 6, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 6, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 6, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 6, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 6, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 6, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 6, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 7, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 7, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 7, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 7, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 7, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 7, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 7, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 7, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 7, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 7, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 7, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 8, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 8, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 8, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 8, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 8, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 8, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 8, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 8, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 8, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 8, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 8, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 8, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 8, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 8, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 8, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 8, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 8, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 8, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 5, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 3, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 3, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 3, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 3, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 4, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 4, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 4, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 4, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 5, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 5, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 5, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 5, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 5, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 5, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 5, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 5, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 5, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 5, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 5, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 5, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 5, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 5, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 5, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 6, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 6, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 6, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 6, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 6, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 6, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 6, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 6, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 6, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 6, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 7, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 7, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 7, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 7, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 7, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 7, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 7, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 7, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 7, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 7, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 7, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 7, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 7, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 7, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 7, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 7, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 8, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 8, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 8, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 8, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 8, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 8, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 8, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 8, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 8, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 8, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 8, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 8, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 8, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 8, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 8, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 8, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 8, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 8, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 8, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 8, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 8, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 8, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 8, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 8, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 6, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 3, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 3, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 3, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 4, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 4, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 4, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 5, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 5, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 5, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 5, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 5, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 5, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 5, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 5, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 5, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 5, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 5, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 5, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 6, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 6, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 6, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 6, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 6, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 6, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 6, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 6, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 6, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 6, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 6, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 6, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 6, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 6, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 6, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 6, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 6, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 8, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 8, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 8, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 8, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 8, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 8, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 8, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 8, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 8, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 8, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 8, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 8, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 8, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 8, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 8, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 8, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 8, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 8, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 8, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 7, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 3, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 3, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 3, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 3, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 3, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 4, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 4, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 4, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 4, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 4, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 5, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 5, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 5, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 5, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 5, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 5, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 5, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 5, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 5, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 5, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 5, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 5, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 5, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 5, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 5, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 5, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 5, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 5, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 5, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 6, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 6, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 6, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 6, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 6, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 6, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 6, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 6, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 6, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 6, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 6, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 6, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 6, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 6, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 6, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 6, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 6, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 6, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 6, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 6, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 6, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 6, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 6, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 6, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 6, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 7, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 7, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 7, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 7, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 7, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 7, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 7, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 7, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 7, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 7, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 7, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 7, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 7, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 7, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 7, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 7, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 7, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 7, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 7, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 8, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 8, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 8, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 8, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 8, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 8, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 8, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 8, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 8, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 8, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 8, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 8, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 8, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 8, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 8, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 8, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 8, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 8, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 8, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 8, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 8, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 8, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 8, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 8, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 8, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 8, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 8, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 3, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 3, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 3, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 3, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 3, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 4, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 4, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 4, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 4, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 4, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 5, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 5, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 5, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 5, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 5, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 5, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 5, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 5, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 5, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 5, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 5, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 5, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 5, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 5, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 5, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 5, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 5, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 5, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 5, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 6, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 6, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 6, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 6, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 6, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 6, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 6, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 6, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 6, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 6, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 6, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 6, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 6, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 6, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 6, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 6, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 6, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 6, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 6, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 6, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 6, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 6, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 6, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 6, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 6, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 7, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 7, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 7, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 7, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 7, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 7, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 7, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 7, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 7, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 7, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 7, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 7, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 7, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 7, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 7, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 7, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 7, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 7, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 7, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 8, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 8, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 8, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 8, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 8, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 8, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 8, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 8, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 8, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 8, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 8, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 8, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 8, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 8, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 8, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 8, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 8, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 8, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 8, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 8, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 8, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 8, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 8, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 8, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 8, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 8, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 8, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 7, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 5, 3, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 3, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 6, 3, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 3, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 3, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 3, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 3, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 3, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 3, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 3, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 3, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 3, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 3, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 3, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 3, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 3, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 5, 4, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 5, 4, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 6, 4, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 6, 4, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 6, 4, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 6, 4, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 7, 4, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 7, 4, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 7, 4, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 7, 4, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 7, 4, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 8, 4, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 8, 4, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 8, 4, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 8, 4, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 8, 4, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 3, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 3, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 4, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 4, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 5, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 5, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 5, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 5, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 6, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 6, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 6, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 6, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 6, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 6, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 6, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 6, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 6, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 6, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 6, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 6, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 6, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 6, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 6, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 7, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 7, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 7, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 7, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 7, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 7, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 7, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 7, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 7, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 7, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 7, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 7, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 7, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 7, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 7, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 7, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 7, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 7, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 8, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 8, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 8, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 8, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 8, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 8, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 8, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 8, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 8, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 8, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 8, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 8, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 8, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 8, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 8, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 8, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 8, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 8, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 5, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 3, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 3, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 3, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 3, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 4, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 4, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 4, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 4, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 5, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 5, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 5, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 5, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 5, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 5, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 5, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 5, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 5, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 5, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 5, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 5, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 5, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 5, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 5, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 6, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 6, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 6, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 6, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 6, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 6, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 6, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 6, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 6, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 6, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 6, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 7, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 7, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 7, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 7, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 7, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 7, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 7, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 7, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 7, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 7, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 7, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 7, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 7, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 7, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 7, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 7, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 7, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 7, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 7, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 7, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 7, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 7, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 7, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 7, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 8, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 8, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 8, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 8, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 8, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 8, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 8, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 8, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 8, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 8, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 8, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 8, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 8, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 8, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 8, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 8, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 8, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 8, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 8, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 8, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 8, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 8, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 8, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 8, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 6, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 3, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 3, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 3, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 3, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 3, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 4, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 4, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 4, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 4, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 4, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 5, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 5, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 5, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 5, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 5, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 5, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 5, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 5, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 5, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 5, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 5, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 5, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 5, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 5, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 5, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 5, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 5, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 5, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 5, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 6, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 6, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 6, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 6, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 6, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 6, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 6, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 6, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 6, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 6, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 6, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 6, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 6, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 6, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 6, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 6, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 6, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 6, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 6, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 6, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 6, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 6, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 6, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 6, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 6, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 7, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 7, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 7, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 7, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 7, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 7, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 7, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 7, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 7, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 7, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 7, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 7, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 7, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 7, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 7, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 7, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 7, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 7, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 7, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 8, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 8, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 8, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 8, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 8, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 8, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 8, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 8, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 8, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 8, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 8, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 8, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 8, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 8, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 8, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 8, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 8, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 8, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 8, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 8, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 8, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 8, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 8, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 8, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 8, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 8, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 8, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 7, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 3, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 3, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 3, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 3, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 3, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 4, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 4, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 4, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 4, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 4, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 5, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 5, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 5, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 5, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 5, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 5, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 5, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 5, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 5, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 5, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 5, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 5, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 5, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 5, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 5, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 5, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 5, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 5, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 5, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 6, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 6, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 6, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 6, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 6, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 6, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 6, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 6, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 6, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 6, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 6, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 6, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 6, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 6, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 6, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 6, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 6, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 6, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 6, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 6, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 6, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 6, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 6, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 6, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 6, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 7, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 7, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 7, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 7, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 7, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 7, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 7, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 7, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 7, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 7, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 7, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 7, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 7, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 7, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 7, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 7, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 7, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 7, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 7, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 7, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 7, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 7, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 7, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 7, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 7, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 7, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 7, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 3, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 3, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 3, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 3, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 3, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 4, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 4, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 4, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 4, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 4, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 5, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 5, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 5, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 5, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 5, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 5, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 5, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 5, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 5, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 5, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 5, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 5, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 5, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 5, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 5, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 5, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 5, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 5, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 5, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 6, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 6, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 6, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 6, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 6, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 6, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 6, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 6, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 6, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 6, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 6, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 6, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 6, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 6, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 6, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 6, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 6, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 6, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 6, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 6, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 6, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 6, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 6, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 6, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 6, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 7, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 7, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 7, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 7, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 7, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 7, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 7, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 7, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 7, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 7, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 7, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 7, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 7, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 7, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 7, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 7, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 7, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 7, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 7, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 7, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 7, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 7, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 7, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 7, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 7, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 7, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 7, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 8, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 8, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 8, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 8, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 8, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 8, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 8, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 8, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 8, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 8, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 8, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 8, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 8, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 8, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 8, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 8, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 8, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 8, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 8, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 8, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 8, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 8, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 8, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 8, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 8, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 8, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 8, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 5, 3, 9, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 5, 3, 9, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 6, 3, 9, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 3, 9, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 6, 3, 9, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 3, 9, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 7, 3, 9, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 3, 9, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 3, 9, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 7, 3, 9, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 3, 9, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 8, 3, 9, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 8, 3, 9, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 8, 3, 9, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 8, 3, 9, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 3, 9, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 5, 4, 9, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 5, 4, 9, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 6, 4, 9, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 6, 4, 9, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 6, 4, 9, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 6, 4, 9, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 7, 4, 9, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 7, 4, 9, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 7, 4, 9, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 7, 4, 9, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 7, 4, 9, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 8, 4, 9, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 8, 4, 9, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 8, 4, 9, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 8, 4, 9, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 8, 4, 9, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 3, 5, 9, 4, 2, 1, 0] + sm_links: *id063 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 3, 5, 9, 4, 2, 1, 0] + sm_links: *id063 diff --git a/tests/generators/fork_choice_generated/block_tree_tree_small.yaml b/tests/generators/fork_choice_generated/block_tree_tree_small.yaml new file mode 100644 index 0000000000..ca4f470ac8 --- /dev/null +++ b/tests/generators/fork_choice_generated/block_tree_tree_small.yaml @@ -0,0 +1,37 @@ +- block_parents: &id002 [0, 0, 1, 2, 3, 2, 1, 0] + sm_links: &id001 + - [0, 1] + - [0, 2] + - [0, 3] +- block_parents: &id003 [0, 0, 1, 2, 2, 3, 1, 0] + sm_links: *id001 +- block_parents: &id005 [0, 0, 1, 2, 3, 3, 1, 0] + sm_links: *id001 +- block_parents: *id002 + sm_links: &id004 + - [0, 1] + - [0, 2] + - [1, 3] +- block_parents: *id003 + sm_links: *id004 +- block_parents: *id005 + sm_links: *id004 +- block_parents: *id002 + sm_links: &id006 + - [0, 1] + - [0, 2] + - [2, 3] +- block_parents: *id003 + sm_links: *id006 +- block_parents: *id005 + sm_links: *id006 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1, 0] + sm_links: &id007 + - [0, 1] + - [0, 2] + - [2, 3] + - [3, 4] +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 5, 4, 3, 2, 1, 0] + sm_links: *id007 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 5, 4, 3, 2, 1, 0] + sm_links: *id007 diff --git a/tests/generators/fork_choice_generated/block_tree_tree_smoke.yaml b/tests/generators/fork_choice_generated/block_tree_tree_smoke.yaml new file mode 100644 index 0000000000..a2a58b7eae --- /dev/null +++ b/tests/generators/fork_choice_generated/block_tree_tree_smoke.yaml @@ -0,0 +1,312 @@ +- block_parents: &id002 [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1, 0] + sm_links: &id001 + - [0, 1] + - [0, 2] + - [0, 3] +- block_parents: &id003 [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 5, 4, 3, 2, 1, 0] + sm_links: *id001 +- block_parents: *id002 + sm_links: &id004 + - [0, 1] + - [0, 2] + - [0, 4] +- block_parents: *id003 + sm_links: *id004 +- block_parents: *id002 + sm_links: &id005 + - [0, 1] + - [0, 2] + - [1, 3] +- block_parents: *id003 + sm_links: *id005 +- block_parents: *id002 + sm_links: &id006 + - [0, 1] + - [0, 2] + - [2, 3] +- block_parents: *id003 + sm_links: *id006 +- block_parents: *id002 + sm_links: &id007 + - [0, 1] + - [0, 2] + - [1, 4] +- block_parents: *id003 + sm_links: *id007 +- block_parents: *id002 + sm_links: &id008 + - [0, 1] + - [0, 2] + - [2, 4] +- block_parents: *id003 + sm_links: *id008 +- block_parents: *id002 + sm_links: &id009 + - [0, 1] + - [0, 3] + - [0, 4] +- block_parents: *id003 + sm_links: *id009 +- block_parents: *id002 + sm_links: &id010 + - [0, 2] + - [0, 3] + - [0, 4] +- block_parents: *id003 + sm_links: *id010 +- block_parents: *id002 + sm_links: &id011 + - [0, 1] + - [0, 3] + - [1, 4] +- block_parents: *id003 + sm_links: *id011 +- block_parents: *id002 + sm_links: &id012 + - [0, 1] + - [0, 3] + - [3, 4] +- block_parents: *id003 + sm_links: *id012 +- block_parents: *id002 + sm_links: &id013 + - [0, 2] + - [0, 3] + - [2, 4] +- block_parents: *id003 + sm_links: *id013 +- block_parents: *id002 + sm_links: &id014 + - [0, 2] + - [0, 3] + - [3, 4] +- block_parents: *id003 + sm_links: *id014 +- block_parents: *id002 + sm_links: &id015 + - [0, 1] + - [1, 3] + - [1, 4] +- block_parents: *id003 + sm_links: *id015 +- block_parents: *id002 + sm_links: &id016 + - [0, 1] + - [1, 3] + - [3, 4] +- block_parents: *id003 + sm_links: *id016 +- block_parents: *id002 + sm_links: &id017 + - [0, 2] + - [2, 3] + - [2, 4] +- block_parents: *id003 + sm_links: *id017 +- block_parents: *id002 + sm_links: &id018 + - [0, 2] + - [2, 3] + - [3, 4] +- block_parents: *id003 + sm_links: *id018 +- block_parents: [0, 0, 1, 0, 0] + sm_links: &id019 + - [0, 1] + - [0, 2] + - [2, 3] + - [3, 4] +- block_parents: [0, 0, 0, 1, 0] + sm_links: *id019 +- block_parents: [0, 0, 1, 1, 0] + sm_links: *id019 +- block_parents: [0, 0, 0, 2, 0] + sm_links: *id019 +- block_parents: [0, 0, 1, 2, 0] + sm_links: *id019 +- block_parents: [0, 0, 0, 0, 1] + sm_links: *id019 +- block_parents: [0, 0, 1, 0, 1] + sm_links: *id019 +- block_parents: [0, 0, 0, 1, 1] + sm_links: *id019 +- block_parents: [0, 0, 1, 1, 1] + sm_links: *id019 +- block_parents: [0, 0, 0, 2, 1] + sm_links: *id019 +- block_parents: [0, 0, 1, 2, 1] + sm_links: *id019 +- block_parents: [0, 0, 0, 0, 2] + sm_links: *id019 +- block_parents: [0, 0, 1, 0, 2] + sm_links: *id019 +- block_parents: [0, 0, 0, 1, 2] + sm_links: *id019 +- block_parents: [0, 0, 1, 1, 2] + sm_links: *id019 +- block_parents: [0, 0, 0, 2, 2] + sm_links: *id019 +- block_parents: [0, 0, 1, 2, 2] + sm_links: *id019 +- block_parents: [0, 0, 0, 0, 3] + sm_links: *id019 +- block_parents: [0, 0, 1, 0, 3] + sm_links: *id019 +- block_parents: [0, 0, 0, 1, 3] + sm_links: *id019 +- block_parents: [0, 0, 1, 1, 3] + sm_links: *id019 +- block_parents: [0, 0, 0, 2, 3] + sm_links: *id019 +- block_parents: [0, 0, 1, 2, 3] + sm_links: *id019 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0] + sm_links: &id020 + - [0, 1] + - [0, 2] + - [2, 3] + - [3, 4] +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 3, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 3, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 4, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 4, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 4, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 4, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 4, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 4, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 3, 5, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 4, 5, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 4, 5, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 3, 6, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 4, 6, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 4, 6, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 3, 7, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 4, 7, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 4, 7, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 2, 3, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 2, 3, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 2, 3, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 2, 3, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 2, 3, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 4, 3, 3, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 2, 4, 4, 5, 3, 3, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 5, 3, 3, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 2, 4, 4, 6, 3, 3, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 6, 3, 3, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 2, 4, 3, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 3, 4, 3, 1, 0] + sm_links: *id020 diff --git a/tests/generators/fork_choice_generated/block_tree_tree_smoke_2.yaml b/tests/generators/fork_choice_generated/block_tree_tree_smoke_2.yaml new file mode 100644 index 0000000000..907dcd18fd --- /dev/null +++ b/tests/generators/fork_choice_generated/block_tree_tree_smoke_2.yaml @@ -0,0 +1,1252 @@ +- block_parents: &id002 [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1, 0] + sm_links: &id001 + - [0, 1] + - [0, 2] + - [0, 3] + - [0, 4] +- block_parents: &id003 [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 5, 4, 3, 2, 1, 0] + sm_links: *id001 +- block_parents: *id002 + sm_links: &id004 + - [0, 1] + - [0, 2] + - [0, 3] + - [0, 5] +- block_parents: *id003 + sm_links: *id004 +- block_parents: *id002 + sm_links: &id005 + - [0, 1] + - [0, 2] + - [0, 3] + - [1, 4] +- block_parents: *id003 + sm_links: *id005 +- block_parents: *id002 + sm_links: &id006 + - [0, 1] + - [0, 2] + - [0, 3] + - [2, 4] +- block_parents: *id003 + sm_links: *id006 +- block_parents: *id002 + sm_links: &id007 + - [0, 1] + - [0, 2] + - [0, 3] + - [3, 4] +- block_parents: *id003 + sm_links: *id007 +- block_parents: *id002 + sm_links: &id008 + - [0, 1] + - [0, 2] + - [0, 3] + - [1, 5] +- block_parents: *id003 + sm_links: *id008 +- block_parents: *id002 + sm_links: &id009 + - [0, 1] + - [0, 2] + - [0, 3] + - [2, 5] +- block_parents: *id003 + sm_links: *id009 +- block_parents: *id002 + sm_links: &id010 + - [0, 1] + - [0, 2] + - [0, 3] + - [3, 5] +- block_parents: *id003 + sm_links: *id010 +- block_parents: *id002 + sm_links: &id011 + - [0, 1] + - [0, 2] + - [1, 3] + - [1, 4] +- block_parents: *id003 + sm_links: *id011 +- block_parents: *id002 + sm_links: &id012 + - [0, 1] + - [0, 2] + - [1, 3] + - [2, 4] +- block_parents: *id003 + sm_links: *id012 +- block_parents: *id002 + sm_links: &id013 + - [0, 1] + - [0, 2] + - [1, 3] + - [3, 4] +- block_parents: *id003 + sm_links: *id013 +- block_parents: *id002 + sm_links: &id014 + - [0, 1] + - [0, 2] + - [1, 3] + - [1, 5] +- block_parents: *id003 + sm_links: *id014 +- block_parents: *id002 + sm_links: &id015 + - [0, 1] + - [0, 2] + - [1, 3] + - [2, 5] +- block_parents: *id003 + sm_links: *id015 +- block_parents: *id002 + sm_links: &id016 + - [0, 1] + - [0, 2] + - [1, 3] + - [3, 5] +- block_parents: *id003 + sm_links: *id016 +- block_parents: *id002 + sm_links: &id017 + - [0, 1] + - [0, 2] + - [2, 3] + - [2, 4] +- block_parents: *id003 + sm_links: *id017 +- block_parents: *id002 + sm_links: &id018 + - [0, 1] + - [0, 2] + - [2, 3] + - [3, 4] +- block_parents: *id003 + sm_links: *id018 +- block_parents: *id002 + sm_links: &id019 + - [0, 1] + - [0, 2] + - [2, 3] + - [2, 5] +- block_parents: *id003 + sm_links: *id019 +- block_parents: *id002 + sm_links: &id020 + - [0, 1] + - [0, 2] + - [2, 3] + - [3, 5] +- block_parents: *id003 + sm_links: *id020 +- block_parents: *id002 + sm_links: &id021 + - [0, 1] + - [0, 2] + - [0, 4] + - [0, 5] +- block_parents: *id003 + sm_links: *id021 +- block_parents: *id002 + sm_links: &id022 + - [0, 1] + - [0, 2] + - [0, 4] + - [1, 5] +- block_parents: *id003 + sm_links: *id022 +- block_parents: *id002 + sm_links: &id023 + - [0, 1] + - [0, 2] + - [0, 4] + - [2, 5] +- block_parents: *id003 + sm_links: *id023 +- block_parents: *id002 + sm_links: &id024 + - [0, 1] + - [0, 2] + - [0, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id024 +- block_parents: *id002 + sm_links: &id025 + - [0, 1] + - [0, 2] + - [1, 4] + - [1, 5] +- block_parents: *id003 + sm_links: *id025 +- block_parents: *id002 + sm_links: &id026 + - [0, 1] + - [0, 2] + - [1, 4] + - [2, 5] +- block_parents: *id003 + sm_links: *id026 +- block_parents: *id002 + sm_links: &id027 + - [0, 1] + - [0, 2] + - [1, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id027 +- block_parents: *id002 + sm_links: &id028 + - [0, 1] + - [0, 2] + - [2, 4] + - [2, 5] +- block_parents: *id003 + sm_links: *id028 +- block_parents: *id002 + sm_links: &id029 + - [0, 1] + - [0, 2] + - [2, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id029 +- block_parents: *id002 + sm_links: &id030 + - [0, 1] + - [0, 3] + - [0, 4] + - [0, 5] +- block_parents: *id003 + sm_links: *id030 +- block_parents: *id002 + sm_links: &id031 + - [0, 2] + - [0, 3] + - [0, 4] + - [0, 5] +- block_parents: *id003 + sm_links: *id031 +- block_parents: *id002 + sm_links: &id032 + - [0, 1] + - [0, 3] + - [0, 4] + - [1, 5] +- block_parents: *id003 + sm_links: *id032 +- block_parents: *id002 + sm_links: &id033 + - [0, 1] + - [0, 3] + - [0, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id033 +- block_parents: *id002 + sm_links: &id034 + - [0, 1] + - [0, 3] + - [0, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id034 +- block_parents: *id002 + sm_links: &id035 + - [0, 2] + - [0, 3] + - [0, 4] + - [2, 5] +- block_parents: *id003 + sm_links: *id035 +- block_parents: *id002 + sm_links: &id036 + - [0, 2] + - [0, 3] + - [0, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id036 +- block_parents: *id002 + sm_links: &id037 + - [0, 2] + - [0, 3] + - [0, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id037 +- block_parents: *id002 + sm_links: &id038 + - [0, 1] + - [0, 3] + - [1, 4] + - [1, 5] +- block_parents: *id003 + sm_links: *id038 +- block_parents: *id002 + sm_links: &id039 + - [0, 1] + - [0, 3] + - [1, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id039 +- block_parents: *id002 + sm_links: &id040 + - [0, 1] + - [0, 3] + - [1, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id040 +- block_parents: *id002 + sm_links: &id041 + - [0, 1] + - [0, 3] + - [3, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id041 +- block_parents: *id002 + sm_links: &id042 + - [0, 1] + - [0, 3] + - [3, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id042 +- block_parents: *id002 + sm_links: &id043 + - [0, 2] + - [0, 3] + - [2, 4] + - [2, 5] +- block_parents: *id003 + sm_links: *id043 +- block_parents: *id002 + sm_links: &id044 + - [0, 2] + - [0, 3] + - [2, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id044 +- block_parents: *id002 + sm_links: &id045 + - [0, 2] + - [0, 3] + - [2, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id045 +- block_parents: *id002 + sm_links: &id046 + - [0, 2] + - [0, 3] + - [3, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id046 +- block_parents: *id002 + sm_links: &id047 + - [0, 2] + - [0, 3] + - [3, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id047 +- block_parents: *id002 + sm_links: &id048 + - [0, 1] + - [1, 3] + - [1, 4] + - [1, 5] +- block_parents: *id003 + sm_links: *id048 +- block_parents: *id002 + sm_links: &id049 + - [0, 1] + - [1, 3] + - [1, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id049 +- block_parents: *id002 + sm_links: &id050 + - [0, 1] + - [1, 3] + - [1, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id050 +- block_parents: *id002 + sm_links: &id051 + - [0, 1] + - [1, 3] + - [3, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id051 +- block_parents: *id002 + sm_links: &id052 + - [0, 1] + - [1, 3] + - [3, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id052 +- block_parents: *id002 + sm_links: &id053 + - [0, 2] + - [2, 3] + - [2, 4] + - [2, 5] +- block_parents: *id003 + sm_links: *id053 +- block_parents: *id002 + sm_links: &id054 + - [0, 2] + - [2, 3] + - [2, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id054 +- block_parents: *id002 + sm_links: &id055 + - [0, 2] + - [2, 3] + - [2, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id055 +- block_parents: *id002 + sm_links: &id056 + - [0, 2] + - [2, 3] + - [3, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id056 +- block_parents: *id002 + sm_links: &id057 + - [0, 2] + - [2, 3] + - [3, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id057 +- block_parents: [0, 0, 1, 0, 0, 0] + sm_links: &id058 + - [0, 1] + - [0, 2] + - [2, 3] + - [3, 4] +- block_parents: [0, 0, 0, 1, 0, 0] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 0, 0] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 0, 0] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 0, 0] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 1, 0] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 1, 0] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 1, 0] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 1, 0] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 1, 0] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 1, 0] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 2, 0] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 2, 0] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 2, 0] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 2, 0] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 2, 0] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 2, 0] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 3, 0] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 3, 0] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 3, 0] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 3, 0] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 3, 0] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 3, 0] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 0, 1] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 0, 1] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 0, 1] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 0, 1] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 0, 1] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 0, 1] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 1, 1] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 1, 1] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 1, 1] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 1, 1] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 1, 1] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 1, 1] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 2, 1] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 2, 1] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 2, 1] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 2, 1] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 2, 1] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 2, 1] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 3, 1] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 3, 1] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 3, 1] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 3, 1] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 3, 1] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 3, 1] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 0, 2] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 0, 2] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 0, 2] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 0, 2] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 0, 2] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 0, 2] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 1, 2] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 1, 2] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 1, 2] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 1, 2] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 1, 2] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 1, 2] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 2, 2] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 2, 2] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 2, 2] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 2, 2] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 2, 2] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 2, 2] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 3, 2] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 3, 2] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 3, 2] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 3, 2] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 3, 2] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 3, 2] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 0, 3] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 0, 3] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 0, 3] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 0, 3] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 0, 3] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 0, 3] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 1, 3] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 1, 3] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 1, 3] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 1, 3] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 1, 3] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 1, 3] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 2, 3] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 2, 3] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 2, 3] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 2, 3] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 2, 3] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 2, 3] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 3, 3] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 3, 3] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 3, 3] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 3, 3] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 3, 3] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 3, 3] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 0, 4] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 0, 4] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 0, 4] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 0, 4] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 0, 4] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 0, 4] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 1, 4] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 1, 4] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 1, 4] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 1, 4] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 1, 4] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 1, 4] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 2, 4] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 2, 4] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 2, 4] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 2, 4] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 2, 4] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 2, 4] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 3, 4] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 3, 4] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 3, 4] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 3, 4] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 3, 4] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 3, 4] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0] + sm_links: &id059 + - [0, 1] + - [0, 2] + - [2, 3] + - [3, 4] +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 3, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 3, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 4, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 4, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 4, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 4, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 4, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 4, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 3, 5, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 4, 5, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 4, 5, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 3, 6, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 4, 6, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 4, 6, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 3, 7, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 4, 7, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 4, 7, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 2, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 2, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 2, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 2, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 2, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 4, 3, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 4, 5, 3, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 5, 3, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 4, 6, 3, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 6, 3, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 2, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 3, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 4, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 4, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 2, 5, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 3, 5, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 4, 5, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 4, 5, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 5, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 5, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 5, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 2, 6, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 3, 6, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 4, 6, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 4, 6, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 6, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 6, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 6, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 2, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 2, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 4, 3, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 3, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 2, 4, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 3, 4, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 4, 4, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 4, 4, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 4, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 4, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 4, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 2, 5, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 3, 5, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 4, 5, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 4, 5, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 4, 5, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 2, 6, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 3, 6, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 4, 6, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 4, 6, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 4, 6, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 6, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 6, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 6, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 2, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 2, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 4, 3, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 3, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 2, 4, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 3, 4, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 4, 4, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 4, 4, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 4, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 4, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 4, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 2, 5, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 3, 5, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 4, 5, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 4, 5, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 4, 5, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 5, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 5, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 5, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 2, 6, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 3, 6, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 4, 6, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 4, 6, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 4, 6, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 6, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 6, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 6, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 2, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 2, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 4, 3, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 3, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 2, 4, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 3, 4, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 4, 4, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 4, 4, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 4, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 4, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 4, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 2, 5, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 3, 5, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 4, 5, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 4, 5, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 4, 5, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 5, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 5, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 5, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 2, 6, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 3, 6, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 4, 6, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 4, 6, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 4, 6, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 6, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 6, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 6, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 2, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 2, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 2, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 2, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 2, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 2, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 2, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 2, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 2, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 2, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 2, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 3, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 4, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 4, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 2, 5, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 3, 5, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 4, 5, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 4, 5, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 5, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 5, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 5, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 2, 6, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 3, 6, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 4, 6, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 4, 6, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 6, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 6, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 6, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 2, 4, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 3, 4, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 3, 4, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 2, 5, 4, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 3, 5, 4, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 3, 5, 4, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 5, 4, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 5, 4, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 2, 6, 4, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 3, 6, 4, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 3, 6, 4, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 6, 4, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 6, 4, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 2, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 2, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 2, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 2, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 2, 3, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 3, 3, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 4, 3, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 4, 3, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 3, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 3, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 3, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 2, 4, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 3, 4, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 3, 4, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 4, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 4, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 2, 5, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 2, 5, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 3, 5, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 3, 5, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 3, 5, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 4, 5, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 4, 5, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 2, 6, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 2, 6, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 3, 6, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 3, 6, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 3, 6, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 4, 6, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 4, 6, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 6, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 6, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 6, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 2, 6, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 2, 6, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 2, 6, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 2, 6, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 2, 3, 6, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 3, 3, 6, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 4, 3, 6, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 4, 3, 6, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 3, 6, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 3, 6, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 3, 6, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 2, 4, 6, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 3, 4, 6, 4, 1, 0] + sm_links: *id059 From 255f01e297735e3a428e8ba39f53887e2ad329ea Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Wed, 15 May 2024 20:12:21 +0400 Subject: [PATCH 066/111] add block_tree_other instances --- .../block_tree_other.yaml | 20 +++++++++++++++++++ .../block_tree_other_small.yaml | 10 ++++++++++ .../block_tree_other_smoke.yaml | 12 +++++++++++ 3 files changed, 42 insertions(+) create mode 100644 tests/generators/fork_choice_generated/block_tree_other.yaml create mode 100644 tests/generators/fork_choice_generated/block_tree_other_small.yaml create mode 100644 tests/generators/fork_choice_generated/block_tree_other_smoke.yaml diff --git a/tests/generators/fork_choice_generated/block_tree_other.yaml b/tests/generators/fork_choice_generated/block_tree_other.yaml new file mode 100644 index 0000000000..ffdc7dc409 --- /dev/null +++ b/tests/generators/fork_choice_generated/block_tree_other.yaml @@ -0,0 +1,20 @@ +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0] + sm_links: &id001 + - [0, 1] + - [0, 2] + - [2, 3] + - [3, 4] +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 3, 2, 1, 0] + sm_links: *id001 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 2, 1, 0] + sm_links: *id001 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 3, 2, 1, 0] + sm_links: *id001 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 2, 1, 0] + sm_links: *id001 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 4, 2, 1, 0] + sm_links: *id001 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 4, 2, 1, 0] + sm_links: *id001 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 4, 2, 1, 0] + sm_links: *id001 diff --git a/tests/generators/fork_choice_generated/block_tree_other_small.yaml b/tests/generators/fork_choice_generated/block_tree_other_small.yaml new file mode 100644 index 0000000000..edd147b557 --- /dev/null +++ b/tests/generators/fork_choice_generated/block_tree_other_small.yaml @@ -0,0 +1,10 @@ +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0] + sm_links: &id001 + - [0, 1] + - [0, 2] + - [2, 3] + - [3, 4] +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 3, 2, 1, 0] + sm_links: *id001 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 2, 1, 0] + sm_links: *id001 diff --git a/tests/generators/fork_choice_generated/block_tree_other_smoke.yaml b/tests/generators/fork_choice_generated/block_tree_other_smoke.yaml new file mode 100644 index 0000000000..e39c4ebc06 --- /dev/null +++ b/tests/generators/fork_choice_generated/block_tree_other_smoke.yaml @@ -0,0 +1,12 @@ +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0] + sm_links: &id001 + - [0, 1] + - [0, 2] + - [2, 3] + - [3, 4] +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 3, 2, 1, 0] + sm_links: *id001 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 2, 1, 0] + sm_links: *id001 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 3, 2, 1, 0] + sm_links: *id001 From 50df903b56f2f7f4c0a58599387697a951eb29d5 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Wed, 15 May 2024 20:12:48 +0400 Subject: [PATCH 067/111] fix typos --- .../fork_choice_generated/tg_standard.yaml | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/generators/fork_choice_generated/tg_standard.yaml b/tests/generators/fork_choice_generated/tg_standard.yaml index a99ee7450c..396db66302 100644 --- a/tests/generators/fork_choice_generated/tg_standard.yaml +++ b/tests/generators/fork_choice_generated/tg_standard.yaml @@ -2,31 +2,31 @@ block_tree_test: test_type: block_tree instances: block_tree_tree.yaml # 1024 seed: 123 - nr_variations: 1 # 2 - nr_mutations: 0 # 1 + nr_variations: 2 + nr_mutations: 1 block_weight_test: test_type: block_tree instances: block_tree_other.yaml # 8 seed: 123 - nr_variations: 1 # 64 - nr_mutations: 0 # 7 + nr_variations: 64 + nr_mutations: 7 attester_slashing_test: test_type: block_tree instances: block_tree_other.yaml # 8 seed: 123 - nr_variations: 1 # 16 - nr_mutations: 0 # 7 + nr_variations: 16 + nr_mutations: 7 with_attester_slashings: true invalid_message_test: test_type: block_tree instances: block_tree_other.yaml # 8 seed: 123 - nr_variations: 1 # 32 - nr_mutations: 0 # 3 + nr_variations: 32 + nr_mutations: 3 with_invalid_messages: true block_cover_test: test_type: block_cover instances: block_cover.yaml # 60 seed: 456 - nr_variations: 1 # 5 - nr_mutations: 0 # 9 + nr_variations: 5 + nr_mutations: 9 From 6f08518bbbad7ccbfe98f9a726d09500b2a16cf6 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Wed, 15 May 2024 21:10:55 +0400 Subject: [PATCH 068/111] put test configs into separate directories --- .../generate_test_instances.py | 22 +++++++++---------- .../block_cover.yaml} | 0 .../block_tree_other.yaml} | 0 .../block_tree_tree.yaml} | 0 .../{tg_small.yaml => small/test_gen.yaml} | 10 ++++----- .../block_cover.yaml} | 0 .../block_tree_other.yaml} | 0 .../block_tree_tree.yaml} | 0 .../block_tree_tree_2.yaml} | 0 .../{tg_smoke.yaml => smoke/test_gen.yaml} | 10 ++++----- .../{ => standard}/block_cover.yaml | 0 .../{ => standard}/block_tree_other.yaml | 0 .../{ => standard}/block_tree_tree.yaml | 0 .../{ => standard}/block_tree_tree_2.yaml | 0 .../test_gen.yaml} | 0 15 files changed, 21 insertions(+), 21 deletions(-) rename tests/generators/fork_choice_generated/{block_cover_small.yaml => small/block_cover.yaml} (100%) rename tests/generators/fork_choice_generated/{block_tree_other_small.yaml => small/block_tree_other.yaml} (100%) rename tests/generators/fork_choice_generated/{block_tree_tree_small.yaml => small/block_tree_tree.yaml} (100%) rename tests/generators/fork_choice_generated/{tg_small.yaml => small/test_gen.yaml} (70%) rename tests/generators/fork_choice_generated/{block_cover_smoke.yaml => smoke/block_cover.yaml} (100%) rename tests/generators/fork_choice_generated/{block_tree_other_smoke.yaml => smoke/block_tree_other.yaml} (100%) rename tests/generators/fork_choice_generated/{block_tree_tree_smoke.yaml => smoke/block_tree_tree.yaml} (100%) rename tests/generators/fork_choice_generated/{block_tree_tree_smoke_2.yaml => smoke/block_tree_tree_2.yaml} (100%) rename tests/generators/fork_choice_generated/{tg_smoke.yaml => smoke/test_gen.yaml} (70%) rename tests/generators/fork_choice_generated/{ => standard}/block_cover.yaml (100%) rename tests/generators/fork_choice_generated/{ => standard}/block_tree_other.yaml (100%) rename tests/generators/fork_choice_generated/{ => standard}/block_tree_tree.yaml (100%) rename tests/generators/fork_choice_generated/{ => standard}/block_tree_tree_2.yaml (100%) rename tests/generators/fork_choice_generated/{tg_standard.yaml => standard/test_gen.yaml} (100%) diff --git a/tests/generators/fork_choice_generated/generate_test_instances.py b/tests/generators/fork_choice_generated/generate_test_instances.py index 5825898a16..28f05ff9a4 100644 --- a/tests/generators/fork_choice_generated/generate_test_instances.py +++ b/tests/generators/fork_choice_generated/generate_test_instances.py @@ -119,7 +119,7 @@ def generate_block_cover(params): ################### 'block_tree_tree_small': { - 'out_path': 'block_tree_tree_small.yaml', + 'out_path': 'small/block_tree_tree.yaml', 'models': ['sm_link', 'block_tree'], 'params': [ ({'anchor_epoch': 0, 'number_of_epochs': 4, 'number_of_links': 3}, {'number_of_blocks': 8, 'max_children': 2, 'number_of_solutions': 3}), @@ -127,14 +127,14 @@ def generate_block_cover(params): ] }, 'block_tree_other_small': { - 'out_path': 'block_tree_other_small.yaml', + 'out_path': 'small/block_tree_other.yaml', 'models': ['sm_link', 'block_tree'], 'params': [ ([{'sm_links': [[0, 1], [0, 2], [2, 3], [3, 4]]}], {'number_of_blocks': 12, 'max_children': 2, 'number_of_solutions': 3}), ] }, 'block_cover_small': { - 'out_path': 'block_cover_small.yaml', + 'out_path': 'small/block_cover.yaml', 'models': ['block_cover'], 'params': [ ({'anchor_epoch': 0, 'number_of_solutions': 1},), @@ -147,7 +147,7 @@ def generate_block_cover(params): ################### 'block_tree_tree_smoke': { - 'out_path': 'block_tree_tree_smoke.yaml', + 'out_path': 'smoke/block_tree_tree.yaml', 'models': ['sm_link', 'block_tree'], 'params': [ ({'anchor_epoch': 0, 'number_of_epochs': 5, 'number_of_links': 3}, {'number_of_blocks': 16, 'max_children': 2, 'number_of_solutions': 2}), @@ -156,7 +156,7 @@ def generate_block_cover(params): ] }, 'block_tree_tree_smoke_2': { - 'out_path': 'block_tree_tree_smoke_2.yaml', + 'out_path': 'smoke/block_tree_tree_2.yaml', 'models': ['sm_link', 'block_tree'], 'params': [ ({'anchor_epoch': 0, 'number_of_epochs': 6, 'number_of_links': 4}, {'number_of_blocks': 16, 'max_children': 2, 'number_of_solutions': 2}), @@ -165,14 +165,14 @@ def generate_block_cover(params): ] }, 'block_tree_other_smoke': { - 'out_path': 'block_tree_other_smoke.yaml', + 'out_path': 'smoke/block_tree_other.yaml', 'models': ['sm_link', 'block_tree'], 'params': [ ([{'sm_links': [[0, 1], [0, 2], [2, 3], [3, 4]]}], {'number_of_blocks': 12, 'max_children': 2, 'number_of_solutions': 4}), ] }, 'block_cover_smoke': { - 'out_path': 'block_cover_smoke.yaml', + 'out_path': 'smoke/block_cover.yaml', 'models': ['block_cover'], 'params': [ ({'anchor_epoch': 0, 'number_of_solutions': 2},), @@ -185,7 +185,7 @@ def generate_block_cover(params): ###################### 'block_tree_tree': { - 'out_path': 'block_tree_tree.yaml', + 'out_path': 'standard/block_tree_tree.yaml', 'models': ['sm_link', 'block_tree'], 'params': [ ({'anchor_epoch': 0, 'number_of_epochs': 6, 'number_of_links': 4}, {'number_of_blocks': 16, 'max_children': 2, 'number_of_solutions': 5}), @@ -195,7 +195,7 @@ def generate_block_cover(params): ] }, 'block_tree_tree_2': { - 'out_path': 'block_tree_tree_2.yaml', + 'out_path': 'standard/block_tree_tree_2.yaml', 'models': ['sm_link', 'block_tree'], 'params': [ ({'anchor_epoch': 0, 'number_of_epochs': 6, 'number_of_links': 4}, {'number_of_blocks': 16, 'max_children': 2, 'number_of_solutions': 5}), @@ -205,14 +205,14 @@ def generate_block_cover(params): ] }, 'block_tree_other': { - 'out_path': 'block_tree_other.yaml', + 'out_path': 'standard/block_tree_other.yaml', 'models': ['sm_link', 'block_tree'], 'params': [ ([{'sm_links': [[0, 1], [0, 2], [2, 3], [3, 4]]}], {'number_of_blocks': 12, 'max_children': 2, 'number_of_solutions': 8}), ] }, 'block_cover': { - 'out_path': 'block_cover.yaml', + 'out_path': 'standard/block_cover.yaml', 'models': ['block_cover'], 'params': [ ({'anchor_epoch': 0, 'number_of_solutions': 2},), diff --git a/tests/generators/fork_choice_generated/block_cover_small.yaml b/tests/generators/fork_choice_generated/small/block_cover.yaml similarity index 100% rename from tests/generators/fork_choice_generated/block_cover_small.yaml rename to tests/generators/fork_choice_generated/small/block_cover.yaml diff --git a/tests/generators/fork_choice_generated/block_tree_other_small.yaml b/tests/generators/fork_choice_generated/small/block_tree_other.yaml similarity index 100% rename from tests/generators/fork_choice_generated/block_tree_other_small.yaml rename to tests/generators/fork_choice_generated/small/block_tree_other.yaml diff --git a/tests/generators/fork_choice_generated/block_tree_tree_small.yaml b/tests/generators/fork_choice_generated/small/block_tree_tree.yaml similarity index 100% rename from tests/generators/fork_choice_generated/block_tree_tree_small.yaml rename to tests/generators/fork_choice_generated/small/block_tree_tree.yaml diff --git a/tests/generators/fork_choice_generated/tg_small.yaml b/tests/generators/fork_choice_generated/small/test_gen.yaml similarity index 70% rename from tests/generators/fork_choice_generated/tg_small.yaml rename to tests/generators/fork_choice_generated/small/test_gen.yaml index 297f62849d..140f705230 100644 --- a/tests/generators/fork_choice_generated/tg_small.yaml +++ b/tests/generators/fork_choice_generated/small/test_gen.yaml @@ -1,32 +1,32 @@ block_tree_test: test_type: block_tree - instances: block_tree_tree_small.yaml # 12 + instances: block_tree_tree.yaml # 12 seed: 123 nr_variations: 2 nr_mutations: 1 block_weight_test: test_type: block_tree - instances: block_tree_other_small.yaml # 3 + instances: block_tree_other.yaml # 3 seed: 123 nr_variations: 5 nr_mutations: 0 attester_slashing_test: test_type: block_tree - instances: block_tree_other_small.yaml # 3 + instances: block_tree_other.yaml # 3 seed: 123 nr_variations: 2 nr_mutations: 1 with_attester_slashings: true invalid_message_test: test_type: block_tree - instances: block_tree_other_small.yaml # 3 + instances: block_tree_other.yaml # 3 seed: 123 nr_variations: 2 nr_mutations: 1 with_invalid_messages: true block_cover_test: test_type: block_cover - instances: block_cover_small.yaml # 12 + instances: block_cover.yaml # 12 seed: 456 nr_variations: 2 nr_mutations: 1 diff --git a/tests/generators/fork_choice_generated/block_cover_smoke.yaml b/tests/generators/fork_choice_generated/smoke/block_cover.yaml similarity index 100% rename from tests/generators/fork_choice_generated/block_cover_smoke.yaml rename to tests/generators/fork_choice_generated/smoke/block_cover.yaml diff --git a/tests/generators/fork_choice_generated/block_tree_other_smoke.yaml b/tests/generators/fork_choice_generated/smoke/block_tree_other.yaml similarity index 100% rename from tests/generators/fork_choice_generated/block_tree_other_smoke.yaml rename to tests/generators/fork_choice_generated/smoke/block_tree_other.yaml diff --git a/tests/generators/fork_choice_generated/block_tree_tree_smoke.yaml b/tests/generators/fork_choice_generated/smoke/block_tree_tree.yaml similarity index 100% rename from tests/generators/fork_choice_generated/block_tree_tree_smoke.yaml rename to tests/generators/fork_choice_generated/smoke/block_tree_tree.yaml diff --git a/tests/generators/fork_choice_generated/block_tree_tree_smoke_2.yaml b/tests/generators/fork_choice_generated/smoke/block_tree_tree_2.yaml similarity index 100% rename from tests/generators/fork_choice_generated/block_tree_tree_smoke_2.yaml rename to tests/generators/fork_choice_generated/smoke/block_tree_tree_2.yaml diff --git a/tests/generators/fork_choice_generated/tg_smoke.yaml b/tests/generators/fork_choice_generated/smoke/test_gen.yaml similarity index 70% rename from tests/generators/fork_choice_generated/tg_smoke.yaml rename to tests/generators/fork_choice_generated/smoke/test_gen.yaml index 32dfe4dcd5..2b746e77ec 100644 --- a/tests/generators/fork_choice_generated/tg_smoke.yaml +++ b/tests/generators/fork_choice_generated/smoke/test_gen.yaml @@ -1,32 +1,32 @@ block_tree_test: test_type: block_tree - instances: block_tree_tree_smoke.yaml # 128 + instances: block_tree_tree.yaml # 128 seed: 123 nr_variations: 2 nr_mutations: 1 block_weight_test: test_type: block_tree - instances: block_tree_other_smoke.yaml # 4 + instances: block_tree_other.yaml # 4 seed: 123 nr_variations: 32 nr_mutations: 3 attester_slashing_test: test_type: block_tree - instances: block_tree_other_smoke.yaml # 4 + instances: block_tree_other.yaml # 4 seed: 123 nr_variations: 8 nr_mutations: 3 with_attester_slashings: true invalid_message_test: test_type: block_tree - instances: block_tree_other_smoke.yaml # 4 + instances: block_tree_other.yaml # 4 seed: 123 nr_variations: 16 nr_mutations: 1 with_invalid_messages: true block_cover_test: test_type: block_cover - instances: block_cover_smoke.yaml # 24 + instances: block_cover.yaml # 24 seed: 456 nr_variations: 2 nr_mutations: 3 diff --git a/tests/generators/fork_choice_generated/block_cover.yaml b/tests/generators/fork_choice_generated/standard/block_cover.yaml similarity index 100% rename from tests/generators/fork_choice_generated/block_cover.yaml rename to tests/generators/fork_choice_generated/standard/block_cover.yaml diff --git a/tests/generators/fork_choice_generated/block_tree_other.yaml b/tests/generators/fork_choice_generated/standard/block_tree_other.yaml similarity index 100% rename from tests/generators/fork_choice_generated/block_tree_other.yaml rename to tests/generators/fork_choice_generated/standard/block_tree_other.yaml diff --git a/tests/generators/fork_choice_generated/block_tree_tree.yaml b/tests/generators/fork_choice_generated/standard/block_tree_tree.yaml similarity index 100% rename from tests/generators/fork_choice_generated/block_tree_tree.yaml rename to tests/generators/fork_choice_generated/standard/block_tree_tree.yaml diff --git a/tests/generators/fork_choice_generated/block_tree_tree_2.yaml b/tests/generators/fork_choice_generated/standard/block_tree_tree_2.yaml similarity index 100% rename from tests/generators/fork_choice_generated/block_tree_tree_2.yaml rename to tests/generators/fork_choice_generated/standard/block_tree_tree_2.yaml diff --git a/tests/generators/fork_choice_generated/tg_standard.yaml b/tests/generators/fork_choice_generated/standard/test_gen.yaml similarity index 100% rename from tests/generators/fork_choice_generated/tg_standard.yaml rename to tests/generators/fork_choice_generated/standard/test_gen.yaml From 6af67eb8a23e8eb3d814f12abded8719424ff1cb Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 29 May 2024 11:44:17 +0600 Subject: [PATCH 069/111] Fix test DNA file paths to run test_gen from root dir --- .../fork_choice_generated/small/test_gen.yaml | 10 +++++----- .../fork_choice_generated/smoke/test_gen.yaml | 10 +++++----- .../fork_choice_generated/standard/test_gen.yaml | 10 +++++----- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/generators/fork_choice_generated/small/test_gen.yaml b/tests/generators/fork_choice_generated/small/test_gen.yaml index 140f705230..d14ed5cd54 100644 --- a/tests/generators/fork_choice_generated/small/test_gen.yaml +++ b/tests/generators/fork_choice_generated/small/test_gen.yaml @@ -1,32 +1,32 @@ block_tree_test: test_type: block_tree - instances: block_tree_tree.yaml # 12 + instances: small/block_tree_tree.yaml # 12 seed: 123 nr_variations: 2 nr_mutations: 1 block_weight_test: test_type: block_tree - instances: block_tree_other.yaml # 3 + instances: small/block_tree_other.yaml # 3 seed: 123 nr_variations: 5 nr_mutations: 0 attester_slashing_test: test_type: block_tree - instances: block_tree_other.yaml # 3 + instances: small/block_tree_other.yaml # 3 seed: 123 nr_variations: 2 nr_mutations: 1 with_attester_slashings: true invalid_message_test: test_type: block_tree - instances: block_tree_other.yaml # 3 + instances: small/block_tree_other.yaml # 3 seed: 123 nr_variations: 2 nr_mutations: 1 with_invalid_messages: true block_cover_test: test_type: block_cover - instances: block_cover.yaml # 12 + instances: small/block_cover.yaml # 12 seed: 456 nr_variations: 2 nr_mutations: 1 diff --git a/tests/generators/fork_choice_generated/smoke/test_gen.yaml b/tests/generators/fork_choice_generated/smoke/test_gen.yaml index 2b746e77ec..58c0888b48 100644 --- a/tests/generators/fork_choice_generated/smoke/test_gen.yaml +++ b/tests/generators/fork_choice_generated/smoke/test_gen.yaml @@ -1,32 +1,32 @@ block_tree_test: test_type: block_tree - instances: block_tree_tree.yaml # 128 + instances: smoke/block_tree_tree.yaml # 128 seed: 123 nr_variations: 2 nr_mutations: 1 block_weight_test: test_type: block_tree - instances: block_tree_other.yaml # 4 + instances: smoke/block_tree_other.yaml # 4 seed: 123 nr_variations: 32 nr_mutations: 3 attester_slashing_test: test_type: block_tree - instances: block_tree_other.yaml # 4 + instances: smoke/block_tree_other.yaml # 4 seed: 123 nr_variations: 8 nr_mutations: 3 with_attester_slashings: true invalid_message_test: test_type: block_tree - instances: block_tree_other.yaml # 4 + instances: smoke/block_tree_other.yaml # 4 seed: 123 nr_variations: 16 nr_mutations: 1 with_invalid_messages: true block_cover_test: test_type: block_cover - instances: block_cover.yaml # 24 + instances: smoke/block_cover.yaml # 24 seed: 456 nr_variations: 2 nr_mutations: 3 diff --git a/tests/generators/fork_choice_generated/standard/test_gen.yaml b/tests/generators/fork_choice_generated/standard/test_gen.yaml index 396db66302..c53acca101 100644 --- a/tests/generators/fork_choice_generated/standard/test_gen.yaml +++ b/tests/generators/fork_choice_generated/standard/test_gen.yaml @@ -1,32 +1,32 @@ block_tree_test: test_type: block_tree - instances: block_tree_tree.yaml # 1024 + instances: standard/block_tree_tree.yaml # 1024 seed: 123 nr_variations: 2 nr_mutations: 1 block_weight_test: test_type: block_tree - instances: block_tree_other.yaml # 8 + instances: standard/block_tree_other.yaml # 8 seed: 123 nr_variations: 64 nr_mutations: 7 attester_slashing_test: test_type: block_tree - instances: block_tree_other.yaml # 8 + instances: standard/block_tree_other.yaml # 8 seed: 123 nr_variations: 16 nr_mutations: 7 with_attester_slashings: true invalid_message_test: test_type: block_tree - instances: block_tree_other.yaml # 8 + instances: standard/block_tree_other.yaml # 8 seed: 123 nr_variations: 32 nr_mutations: 3 with_invalid_messages: true block_cover_test: test_type: block_cover - instances: block_cover.yaml # 60 + instances: standard/block_cover.yaml # 60 seed: 456 nr_variations: 5 nr_mutations: 9 From 6af1e9e607da8890a09c031dbef02e78c1897dae Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 29 May 2024 11:51:44 +0600 Subject: [PATCH 070/111] Use pyspec_tests suite name for fc generated tests --- tests/generators/fork_choice_generated/instance_generator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/generators/fork_choice_generated/instance_generator.py b/tests/generators/fork_choice_generated/instance_generator.py index 8e4dacadc7..deb90f1dd8 100644 --- a/tests/generators/fork_choice_generated/instance_generator.py +++ b/tests/generators/fork_choice_generated/instance_generator.py @@ -60,7 +60,7 @@ def make_cases_fn() -> Iterable[TestCase]: preset_name=preset_name, runner_name=GENERATOR_NAME, handler_name=test_name, - suite_name='fork_choice', + suite_name='pyspec_tests', case_name=test_name + '_' + str(i) + '_' + str(seed) + '_' + str(j), case_fn=mutation_generator.next_test_case) From c428779e3fdc9fa3f2dfefe79412baa6216d7d5d Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 29 May 2024 12:38:17 +0600 Subject: [PATCH 071/111] Add bls_setting to meta data for fc generated tests --- .../fork_choice_generated/instantiators/block_cover.py | 2 ++ .../fork_choice_generated/instantiators/block_tree.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/tests/generators/fork_choice_generated/instantiators/block_cover.py b/tests/generators/fork_choice_generated/instantiators/block_cover.py index 2ac1a62e40..4ed60521ce 100644 --- a/tests/generators/fork_choice_generated/instantiators/block_cover.py +++ b/tests/generators/fork_choice_generated/instantiators/block_cover.py @@ -18,6 +18,7 @@ attest_to_slot, yield_fork_choice_test_case, ) +from eth2spec.utils import bls def _should_justify_epoch(parents, current_justifications, previous_justifications, block) -> bool: if current_justifications[block]: @@ -214,6 +215,7 @@ def gen_block_cover_test_data(spec, state, model_params, debug, seed) -> (FCTest meta = { 'seed': seed, 'model_params': model_params, + 'bls_setting': 0 if bls.bls_active else 2, } blocks = [ProtocolMessage(block) for block in signed_blocks] diff --git a/tests/generators/fork_choice_generated/instantiators/block_tree.py b/tests/generators/fork_choice_generated/instantiators/block_tree.py index 410c91b518..b627b7ffc9 100644 --- a/tests/generators/fork_choice_generated/instantiators/block_tree.py +++ b/tests/generators/fork_choice_generated/instantiators/block_tree.py @@ -27,6 +27,7 @@ print_epoch, print_block_tree, ) +from eth2spec.utils import bls MAX_JUSTIFICATION_RATE = 99 MIN_JUSTIFICATION_RATE = 91 @@ -564,6 +565,7 @@ def gen_block_tree_test_data(spec, 'seed': seed, 'sm_links': str(sm_links), 'block_parents': str(block_parents), + 'bls_setting': 0 if bls.bls_active else 2, } return FCTestData(meta, anchor_block, anchor_state, From f3a7194ded5a9c476b644752ffdb0397821d05db Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 29 May 2024 13:11:12 +0600 Subject: [PATCH 072/111] Add pseudo randao_reveal for fc generated tests --- .../fork_choice_generated/instantiators/helpers.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/generators/fork_choice_generated/instantiators/helpers.py b/tests/generators/fork_choice_generated/instantiators/helpers.py index 79aaef370b..60756bc7cf 100644 --- a/tests/generators/fork_choice_generated/instantiators/helpers.py +++ b/tests/generators/fork_choice_generated/instantiators/helpers.py @@ -61,6 +61,11 @@ def _get_voting_source(target: spec.Checkpoint) -> spec.Checkpoint: state.slot <= a.data.slot + spec.SLOTS_PER_EPOCH and a.data.source == _get_voting_source(a.data.target)] +def _compute_pseudo_randao_reveal(spec, proposer_index, epoch): + pseudo_vrn = spec.uint64((proposer_index + 1) * epoch) + pseudo_vrn_bytes = spec.uint_to_bytes(pseudo_vrn) + randao_reveal_bytes = bytes(96 - len(pseudo_vrn_bytes)) + pseudo_vrn_bytes + return spec.BLSSignature(randao_reveal_bytes) def produce_block(spec, state, attestations, attester_slashings=[]): """ @@ -76,6 +81,8 @@ def produce_block(spec, state, attestations, attester_slashings=[]): # Create a block with attestations block = build_empty_block(spec, state) + block.body.randao_reveal = _compute_pseudo_randao_reveal( + spec, block.proposer_index, spec.get_current_epoch(state)) for a in attestation_in_block: block.body.attestations.append(a) From b049f90f18a3be25eea3472060be32775bb5cd3a Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 29 May 2024 13:16:08 +0600 Subject: [PATCH 073/111] Replace filtered_block_weights with viable_for_head_roots_and_weights --- .../pyspec/eth2spec/test/helpers/fork_choice.py | 16 ++++++++++------ .../instantiators/helpers.py | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py index f4a6c2a73d..576db928db 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py +++ b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py @@ -285,7 +285,7 @@ def output_head_check(spec, store, test_steps): }) -def output_store_checks(spec, store, test_steps, with_filtered_block_weights=False): +def output_store_checks(spec, store, test_steps, with_viable_for_head_weights=False): checks = { 'time': int(store.time), 'head': get_formatted_head_output(spec, store), @@ -300,12 +300,16 @@ def output_store_checks(spec, store, test_steps, with_filtered_block_weights=Fal 'proposer_boost_root': encode_hex(store.proposer_boost_root), } - if with_filtered_block_weights: - filtered_block_weights = { - encode_hex(filtered_block_root): int(spec.get_weight(store, filtered_block_root)) - for filtered_block_root in spec.get_filtered_block_tree(store).keys() + if with_viable_for_head_weights: + filtered_block_roots = spec.get_filtered_block_tree(store).keys() + leaves_viable_for_head = [root for root in filtered_block_roots + if not any(c for c in filtered_block_roots if store.blocks[c].parent_root == root)] + + viable_for_head_roots_and_weights = { + encode_hex(viable_for_head_root): int(spec.get_weight(store, viable_for_head_root)) + for viable_for_head_root in leaves_viable_for_head } - checks['filtered_block_weights'] = filtered_block_weights + checks['viable_for_head_roots_and_weights'] = viable_for_head_roots_and_weights test_steps.append({'checks': checks}) diff --git a/tests/generators/fork_choice_generated/instantiators/helpers.py b/tests/generators/fork_choice_generated/instantiators/helpers.py index 60756bc7cf..8d91e315e0 100644 --- a/tests/generators/fork_choice_generated/instantiators/helpers.py +++ b/tests/generators/fork_choice_generated/instantiators/helpers.py @@ -295,6 +295,6 @@ def yield_fork_choice_test_case(spec, store, test_data: FCTestData, debug: bool) if debug: print(' head: ' + print_head(spec, store)) - output_store_checks(spec, store, test_steps, with_filtered_block_weights=True) + output_store_checks(spec, store, test_steps, with_viable_for_head_weights=True) yield 'steps', test_steps From de42529bddc9e08305afdb7922bb8acd66b3f141 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 29 May 2024 15:16:16 +0600 Subject: [PATCH 074/111] Fix pseudo randao_reveal computation --- .../generators/fork_choice_generated/instantiators/helpers.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/generators/fork_choice_generated/instantiators/helpers.py b/tests/generators/fork_choice_generated/instantiators/helpers.py index 8d91e315e0..c3250ec599 100644 --- a/tests/generators/fork_choice_generated/instantiators/helpers.py +++ b/tests/generators/fork_choice_generated/instantiators/helpers.py @@ -61,12 +61,14 @@ def _get_voting_source(target: spec.Checkpoint) -> spec.Checkpoint: state.slot <= a.data.slot + spec.SLOTS_PER_EPOCH and a.data.source == _get_voting_source(a.data.target)] + def _compute_pseudo_randao_reveal(spec, proposer_index, epoch): - pseudo_vrn = spec.uint64((proposer_index + 1) * epoch) + pseudo_vrn = spec.uint64((proposer_index + 1) * (epoch + 1)) pseudo_vrn_bytes = spec.uint_to_bytes(pseudo_vrn) randao_reveal_bytes = bytes(96 - len(pseudo_vrn_bytes)) + pseudo_vrn_bytes return spec.BLSSignature(randao_reveal_bytes) + def produce_block(spec, state, attestations, attester_slashings=[]): """ Produces a block including as many attestations as it is possible. From a6af0781777cc8c06d8f022aab13567ba926ff95 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Thu, 30 May 2024 17:42:27 +0400 Subject: [PATCH 075/111] Make MutationGenerator serializeable --- .../fork_choice_generated/mutation_operators.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/generators/fork_choice_generated/mutation_operators.py b/tests/generators/fork_choice_generated/mutation_operators.py index e5ae9facf3..74e41fb819 100644 --- a/tests/generators/fork_choice_generated/mutation_operators.py +++ b/tests/generators/fork_choice_generated/mutation_operators.py @@ -241,8 +241,15 @@ def mk_mutations(spec, seed, num, test_fn, debug=False): class MutatorsGenerator: def __init__(self, spec, seed, num, test_fn, debug=False): - self.iterator = iter(mk_mutations(spec, seed, num, test_fn, debug)) + self.spec = spec + self.seed = seed + self.num = num + self.test_fn = test_fn + self.debug = debug + self.iterator = None def next_test_case(self): + if self.iterator is None: + self.iterator = iter(mk_mutations(self.spec, self.seed, self.num, self.test_fn, self.debug)) _, test_case = next(self.iterator) return test_case From f99592b9bd0c9a9a9359bfa7cad819797218ef2d Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Thu, 30 May 2024 17:44:14 +0400 Subject: [PATCH 076/111] Make TestProviders finer granular --- .../instance_generator.py | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/tests/generators/fork_choice_generated/instance_generator.py b/tests/generators/fork_choice_generated/instance_generator.py index deb90f1dd8..048b65f7b7 100644 --- a/tests/generators/fork_choice_generated/instance_generator.py +++ b/tests/generators/fork_choice_generated/instance_generator.py @@ -1,6 +1,6 @@ from eth2spec.test.helpers.constants import ALTAIR from eth2spec.gen_helpers.gen_base import gen_runner -from eth2spec.test.helpers.constants import MINIMAL +from eth2spec.test.helpers.constants import MINIMAL, MAINNET from eth2spec.test.helpers.specs import spec_targets from eth2spec.gen_helpers.gen_base.gen_typing import TestCase, TestProvider from itertools import product @@ -39,18 +39,19 @@ def prepare_fn() -> None: bls.use_milagro() return - def make_cases_fn() -> Iterable[TestCase]: - seeds = [initial_seed] - if number_of_variations > 1: - rnd = random.Random(initial_seed) - seeds = [rnd.randint(1, 10000) for _ in range(number_of_variations)] - seeds[0] = initial_seed - - for i, solution in enumerate(solutions): - for seed in seeds: - for fork_name in forks: - for preset_name in presets: - spec = spec_targets[preset_name][fork_name] + seeds = [initial_seed] + if number_of_variations > 1: + rnd = random.Random(initial_seed) + seeds = [rnd.randint(1, 10000) for _ in range(number_of_variations)] + seeds[0] = initial_seed + + for fork_name in forks: + for preset_name in presets: + spec = spec_targets[preset_name][fork_name] + + for i, solution in enumerate(solutions): + def make_cases_fn() -> Iterable[TestCase]: + for seed in seeds: mutation_generator = MutatorsGenerator( spec, seed, number_of_mutations, lambda: test_fn(fork_name, preset_name, seed, solution), @@ -64,7 +65,7 @@ def make_cases_fn() -> Iterable[TestCase]: case_name=test_name + '_' + str(i) + '_' + str(seed) + '_' + str(j), case_fn=mutation_generator.next_test_case) - yield TestProvider(prepare=prepare_fn, make_cases=make_cases_fn) + yield TestProvider(prepare=prepare_fn, make_cases=make_cases_fn) def _find_sm_link_solutions(anchor_epoch: int, From 789704f667b7ef3215f0716904cd6cae19193427 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Thu, 30 May 2024 20:35:56 +0400 Subject: [PATCH 077/111] Fixed typo in standard/block_cover config --- .../generate_test_instances.py | 2 +- .../standard/block_cover.yaml | 108 ++++++++++++++++++ 2 files changed, 109 insertions(+), 1 deletion(-) diff --git a/tests/generators/fork_choice_generated/generate_test_instances.py b/tests/generators/fork_choice_generated/generate_test_instances.py index 28f05ff9a4..0c997d8ce1 100644 --- a/tests/generators/fork_choice_generated/generate_test_instances.py +++ b/tests/generators/fork_choice_generated/generate_test_instances.py @@ -215,7 +215,7 @@ def generate_block_cover(params): 'out_path': 'standard/block_cover.yaml', 'models': ['block_cover'], 'params': [ - ({'anchor_epoch': 0, 'number_of_solutions': 2},), + ({'anchor_epoch': 0, 'number_of_solutions': 5},), ({'anchor_epoch': 2, 'number_of_solutions': 5},), ] }, diff --git a/tests/generators/fork_choice_generated/standard/block_cover.yaml b/tests/generators/fork_choice_generated/standard/block_cover.yaml index ae3dcf0289..331079855a 100644 --- a/tests/generators/fork_choice_generated/standard/block_cover.yaml +++ b/tests/generators/fork_choice_generated/standard/block_cover.yaml @@ -1,3 +1,30 @@ +- block_epochs: [0] + current_epoch: 1 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: true} + previous_justifications: [false] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [0] + current_epoch: 1 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: true} + previous_justifications: [false] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [0] + current_epoch: 1 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: true} + previous_justifications: [false] + store_justified_epoch: 0 + target_block: 0 - block_epochs: [0] current_epoch: 1 current_justifications: [false] @@ -34,6 +61,60 @@ previous_justifications: [false, false, false] store_justified_epoch: 0 target_block: 0 +- block_epochs: [0, 1, 1, 1] + current_epoch: 1 + current_justifications: [false, false, false, false] + parents: [0, 0, 0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: true} + previous_justifications: [false, false, false, false] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [0, 1, 1, 1, 1] + current_epoch: 1 + current_justifications: [false, false, false, false, false] + parents: [0, 0, 0, 0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: true} + previous_justifications: [false, false, false, false, false] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [0, 1] + current_epoch: 1 + current_justifications: [false, false] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: true} + previous_justifications: [false, true] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [0] + current_epoch: 3 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: true} + previous_justifications: [false] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [0] + current_epoch: 3 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: true} + previous_justifications: [false] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [0] + current_epoch: 3 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: true} + previous_justifications: [false] + store_justified_epoch: 0 + target_block: 0 - block_epochs: [0] current_epoch: 3 current_justifications: [false] @@ -70,6 +151,33 @@ previous_justifications: [false, true] store_justified_epoch: 0 target_block: 0 +- block_epochs: [0, 1, 1] + current_epoch: 3 + current_justifications: [false, false, false] + parents: [0, 0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: true} + previous_justifications: [false, false, false] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [0, 1, 1] + current_epoch: 3 + current_justifications: [false, false, false] + parents: [0, 0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: true} + previous_justifications: [false, true, false] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [0, 1, 1, 1] + current_epoch: 3 + current_justifications: [false, false, false, false] + parents: [0, 0, 0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: true} + previous_justifications: [false, false, false, false] + store_justified_epoch: 0 + target_block: 0 - block_epochs: [2] current_epoch: 3 current_justifications: [false] From 22b2a906e96ced87211c8c1322f18934ce9c8e7a Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 11 Jun 2024 20:16:09 +0400 Subject: [PATCH 078/111] Fixed a problem with running in multiprocessor node - incompatible results due to using lambdas --- .../fork_choice_generated/instance_generator.py | 5 ++--- .../fork_choice_generated/mutation_operators.py | 9 +++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/tests/generators/fork_choice_generated/instance_generator.py b/tests/generators/fork_choice_generated/instance_generator.py index 048b65f7b7..a3f98b3712 100644 --- a/tests/generators/fork_choice_generated/instance_generator.py +++ b/tests/generators/fork_choice_generated/instance_generator.py @@ -53,9 +53,8 @@ def prepare_fn() -> None: def make_cases_fn() -> Iterable[TestCase]: for seed in seeds: mutation_generator = MutatorsGenerator( - spec, seed, number_of_mutations, - lambda: test_fn(fork_name, preset_name, seed, solution), - debug=debug) + fork_name, preset_name, spec, solution, + seed, number_of_mutations, test_fn, debug=debug) for j in range(1 + number_of_mutations): yield TestCase(fork_name=fork_name, preset_name=preset_name, diff --git a/tests/generators/fork_choice_generated/mutation_operators.py b/tests/generators/fork_choice_generated/mutation_operators.py index 74e41fb819..087dae11f2 100644 --- a/tests/generators/fork_choice_generated/mutation_operators.py +++ b/tests/generators/fork_choice_generated/mutation_operators.py @@ -240,8 +240,11 @@ def mk_mutations(spec, seed, num, test_fn, debug=False): class MutatorsGenerator: - def __init__(self, spec, seed, num, test_fn, debug=False): + def __init__(self, fork, preset, spec, solution, seed, num, test_fn, debug=False): + self.fork = fork + self.preset = preset self.spec = spec + self.solution = solution self.seed = seed self.num = num self.test_fn = test_fn @@ -250,6 +253,8 @@ def __init__(self, spec, seed, num, test_fn, debug=False): def next_test_case(self): if self.iterator is None: - self.iterator = iter(mk_mutations(self.spec, self.seed, self.num, self.test_fn, self.debug)) + def test_inst_fn(): + return self.test_fn(self.fork, self.preset, self.seed, self.solution) + self.iterator = iter(mk_mutations(self.spec, self.seed, self.num, test_inst_fn, self.debug)) _, test_case = next(self.iterator) return test_case From a2c09976b693d001db7a69b5b5e882c20871e699 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 11 Jun 2024 20:41:12 +0400 Subject: [PATCH 079/111] Added shuffling_test (by splitting block_weight_test) --- .../generators/fork_choice_generated/smoke/test_gen.yaml | 8 +++++++- .../fork_choice_generated/standard/test_gen.yaml | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/tests/generators/fork_choice_generated/smoke/test_gen.yaml b/tests/generators/fork_choice_generated/smoke/test_gen.yaml index 58c0888b48..2deea7b017 100644 --- a/tests/generators/fork_choice_generated/smoke/test_gen.yaml +++ b/tests/generators/fork_choice_generated/smoke/test_gen.yaml @@ -9,7 +9,13 @@ block_weight_test: instances: smoke/block_tree_other.yaml # 4 seed: 123 nr_variations: 32 - nr_mutations: 3 + nr_mutations: 1 +shuffling_test: + test_type: block_tree + instances: smoke/block_tree_other.yaml # 4 + seed: 123 + nr_variations: 2 + nr_mutations: 31 attester_slashing_test: test_type: block_tree instances: smoke/block_tree_other.yaml # 4 diff --git a/tests/generators/fork_choice_generated/standard/test_gen.yaml b/tests/generators/fork_choice_generated/standard/test_gen.yaml index c53acca101..3137c28986 100644 --- a/tests/generators/fork_choice_generated/standard/test_gen.yaml +++ b/tests/generators/fork_choice_generated/standard/test_gen.yaml @@ -9,7 +9,13 @@ block_weight_test: instances: standard/block_tree_other.yaml # 8 seed: 123 nr_variations: 64 - nr_mutations: 7 + nr_mutations: 3 +shuffling_test: + test_type: block_tree + instances: standard/block_tree_other.yaml # 8 + seed: 6673 + nr_variations: 1 + nr_mutations: 20 attester_slashing_test: test_type: block_tree instances: standard/block_tree_other.yaml # 8 From bbdae54d8825f92da37ed0cf0d81c6fb04b5fb10 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 11 Jun 2024 20:45:45 +0400 Subject: [PATCH 080/111] renamed small -> tiny, smoke -> small --- .../small/block_cover.yaml | 108 ++ .../small/block_tree_other.yaml | 2 + .../small/block_tree_tree.yaml | 303 +++- .../small/block_tree_tree_2.yaml | 1252 +++++++++++++++++ .../fork_choice_generated/small/test_gen.yaml | 28 +- .../tiny/block_cover.yaml | 108 ++ .../tiny/block_tree_other.yaml | 10 + .../tiny/block_tree_tree.yaml | 37 + .../fork_choice_generated/tiny/test_gen.yaml | 32 + 9 files changed, 1855 insertions(+), 25 deletions(-) create mode 100644 tests/generators/fork_choice_generated/small/block_tree_tree_2.yaml create mode 100644 tests/generators/fork_choice_generated/tiny/block_cover.yaml create mode 100644 tests/generators/fork_choice_generated/tiny/block_tree_other.yaml create mode 100644 tests/generators/fork_choice_generated/tiny/block_tree_tree.yaml create mode 100644 tests/generators/fork_choice_generated/tiny/test_gen.yaml diff --git a/tests/generators/fork_choice_generated/small/block_cover.yaml b/tests/generators/fork_choice_generated/small/block_cover.yaml index aa6d88f7d4..6f8ad60ce6 100644 --- a/tests/generators/fork_choice_generated/small/block_cover.yaml +++ b/tests/generators/fork_choice_generated/small/block_cover.yaml @@ -1,3 +1,12 @@ +- block_epochs: [0] + current_epoch: 1 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: true} + previous_justifications: [false] + store_justified_epoch: 0 + target_block: 0 - block_epochs: [0] current_epoch: 1 current_justifications: [false] @@ -16,6 +25,24 @@ previous_justifications: [false, false] store_justified_epoch: 0 target_block: 0 +- block_epochs: [0, 1, 1] + current_epoch: 1 + current_justifications: [false, false, false] + parents: [0, 0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: true} + previous_justifications: [false, false, false] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [0] + current_epoch: 3 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: true} + previous_justifications: [false] + store_justified_epoch: 0 + target_block: 0 - block_epochs: [0] current_epoch: 3 current_justifications: [false] @@ -34,6 +61,24 @@ previous_justifications: [false, false] store_justified_epoch: 0 target_block: 0 +- block_epochs: [0, 1] + current_epoch: 3 + current_justifications: [false, false] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: true} + previous_justifications: [false, true] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [2] + current_epoch: 3 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false] + store_justified_epoch: 2 + target_block: 0 - block_epochs: [2] current_epoch: 3 current_justifications: [false] @@ -52,6 +97,24 @@ previous_justifications: [false, false] store_justified_epoch: 2 target_block: 0 +- block_epochs: [2, 3, 3] + current_epoch: 3 + current_justifications: [false, false, false] + parents: [0, 0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false, false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2] + current_epoch: 5 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false] + store_justified_epoch: 2 + target_block: 0 - block_epochs: [2] current_epoch: 5 current_justifications: [false] @@ -70,6 +133,15 @@ previous_justifications: [false, false] store_justified_epoch: 2 target_block: 0 +- block_epochs: [2, 3, 3] + current_epoch: 5 + current_justifications: [false, false, false] + parents: [0, 0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, false, false] + store_justified_epoch: 2 + target_block: 0 - block_epochs: [2, 3, 3] current_epoch: 4 current_justifications: [false, false, true] @@ -79,6 +151,15 @@ previous_justifications: [false, false, false] store_justified_epoch: 3 target_block: 1 +- block_epochs: [2, 3, 3] + current_epoch: 4 + current_justifications: [false, false, true] + parents: [0, 0, 0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false, true] + store_justified_epoch: 3 + target_block: 1 - block_epochs: [2, 3] current_epoch: 4 current_justifications: [false, true] @@ -88,6 +169,15 @@ previous_justifications: [false, false] store_justified_epoch: 3 target_block: 0 +- block_epochs: [2, 3] + current_epoch: 4 + current_justifications: [false, true] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, true] + store_justified_epoch: 3 + target_block: 0 - block_epochs: [2, 3, 3] current_epoch: 5 current_justifications: [false, false, true] @@ -97,6 +187,15 @@ previous_justifications: [false, false, false] store_justified_epoch: 3 target_block: 1 +- block_epochs: [2, 3, 3] + current_epoch: 5 + current_justifications: [false, false, true] + parents: [0, 0, 0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, false, true] + store_justified_epoch: 3 + target_block: 1 - block_epochs: [2, 3] current_epoch: 5 current_justifications: [false, true] @@ -106,3 +205,12 @@ previous_justifications: [false, false] store_justified_epoch: 3 target_block: 0 +- block_epochs: [2, 3] + current_epoch: 5 + current_justifications: [false, true] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, true] + store_justified_epoch: 3 + target_block: 0 diff --git a/tests/generators/fork_choice_generated/small/block_tree_other.yaml b/tests/generators/fork_choice_generated/small/block_tree_other.yaml index edd147b557..e39c4ebc06 100644 --- a/tests/generators/fork_choice_generated/small/block_tree_other.yaml +++ b/tests/generators/fork_choice_generated/small/block_tree_other.yaml @@ -8,3 +8,5 @@ sm_links: *id001 - block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 2, 1, 0] sm_links: *id001 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 3, 2, 1, 0] + sm_links: *id001 diff --git a/tests/generators/fork_choice_generated/small/block_tree_tree.yaml b/tests/generators/fork_choice_generated/small/block_tree_tree.yaml index ca4f470ac8..a2a58b7eae 100644 --- a/tests/generators/fork_choice_generated/small/block_tree_tree.yaml +++ b/tests/generators/fork_choice_generated/small/block_tree_tree.yaml @@ -1,21 +1,24 @@ -- block_parents: &id002 [0, 0, 1, 2, 3, 2, 1, 0] +- block_parents: &id002 [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1, 0] sm_links: &id001 - [0, 1] - [0, 2] - [0, 3] -- block_parents: &id003 [0, 0, 1, 2, 2, 3, 1, 0] - sm_links: *id001 -- block_parents: &id005 [0, 0, 1, 2, 3, 3, 1, 0] +- block_parents: &id003 [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 5, 4, 3, 2, 1, 0] sm_links: *id001 - block_parents: *id002 sm_links: &id004 - [0, 1] - [0, 2] - - [1, 3] + - [0, 4] - block_parents: *id003 sm_links: *id004 -- block_parents: *id005 - sm_links: *id004 +- block_parents: *id002 + sm_links: &id005 + - [0, 1] + - [0, 2] + - [1, 3] +- block_parents: *id003 + sm_links: *id005 - block_parents: *id002 sm_links: &id006 - [0, 1] @@ -23,15 +26,287 @@ - [2, 3] - block_parents: *id003 sm_links: *id006 -- block_parents: *id005 - sm_links: *id006 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1, 0] +- block_parents: *id002 sm_links: &id007 - [0, 1] - [0, 2] + - [1, 4] +- block_parents: *id003 + sm_links: *id007 +- block_parents: *id002 + sm_links: &id008 + - [0, 1] + - [0, 2] + - [2, 4] +- block_parents: *id003 + sm_links: *id008 +- block_parents: *id002 + sm_links: &id009 + - [0, 1] + - [0, 3] + - [0, 4] +- block_parents: *id003 + sm_links: *id009 +- block_parents: *id002 + sm_links: &id010 + - [0, 2] + - [0, 3] + - [0, 4] +- block_parents: *id003 + sm_links: *id010 +- block_parents: *id002 + sm_links: &id011 + - [0, 1] + - [0, 3] + - [1, 4] +- block_parents: *id003 + sm_links: *id011 +- block_parents: *id002 + sm_links: &id012 + - [0, 1] + - [0, 3] + - [3, 4] +- block_parents: *id003 + sm_links: *id012 +- block_parents: *id002 + sm_links: &id013 + - [0, 2] + - [0, 3] + - [2, 4] +- block_parents: *id003 + sm_links: *id013 +- block_parents: *id002 + sm_links: &id014 + - [0, 2] + - [0, 3] + - [3, 4] +- block_parents: *id003 + sm_links: *id014 +- block_parents: *id002 + sm_links: &id015 + - [0, 1] + - [1, 3] + - [1, 4] +- block_parents: *id003 + sm_links: *id015 +- block_parents: *id002 + sm_links: &id016 + - [0, 1] + - [1, 3] + - [3, 4] +- block_parents: *id003 + sm_links: *id016 +- block_parents: *id002 + sm_links: &id017 + - [0, 2] + - [2, 3] + - [2, 4] +- block_parents: *id003 + sm_links: *id017 +- block_parents: *id002 + sm_links: &id018 + - [0, 2] + - [2, 3] + - [3, 4] +- block_parents: *id003 + sm_links: *id018 +- block_parents: [0, 0, 1, 0, 0] + sm_links: &id019 + - [0, 1] + - [0, 2] - [2, 3] - [3, 4] -- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 5, 4, 3, 2, 1, 0] - sm_links: *id007 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 5, 4, 3, 2, 1, 0] - sm_links: *id007 +- block_parents: [0, 0, 0, 1, 0] + sm_links: *id019 +- block_parents: [0, 0, 1, 1, 0] + sm_links: *id019 +- block_parents: [0, 0, 0, 2, 0] + sm_links: *id019 +- block_parents: [0, 0, 1, 2, 0] + sm_links: *id019 +- block_parents: [0, 0, 0, 0, 1] + sm_links: *id019 +- block_parents: [0, 0, 1, 0, 1] + sm_links: *id019 +- block_parents: [0, 0, 0, 1, 1] + sm_links: *id019 +- block_parents: [0, 0, 1, 1, 1] + sm_links: *id019 +- block_parents: [0, 0, 0, 2, 1] + sm_links: *id019 +- block_parents: [0, 0, 1, 2, 1] + sm_links: *id019 +- block_parents: [0, 0, 0, 0, 2] + sm_links: *id019 +- block_parents: [0, 0, 1, 0, 2] + sm_links: *id019 +- block_parents: [0, 0, 0, 1, 2] + sm_links: *id019 +- block_parents: [0, 0, 1, 1, 2] + sm_links: *id019 +- block_parents: [0, 0, 0, 2, 2] + sm_links: *id019 +- block_parents: [0, 0, 1, 2, 2] + sm_links: *id019 +- block_parents: [0, 0, 0, 0, 3] + sm_links: *id019 +- block_parents: [0, 0, 1, 0, 3] + sm_links: *id019 +- block_parents: [0, 0, 0, 1, 3] + sm_links: *id019 +- block_parents: [0, 0, 1, 1, 3] + sm_links: *id019 +- block_parents: [0, 0, 0, 2, 3] + sm_links: *id019 +- block_parents: [0, 0, 1, 2, 3] + sm_links: *id019 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0] + sm_links: &id020 + - [0, 1] + - [0, 2] + - [2, 3] + - [3, 4] +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 3, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 3, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 4, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 4, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 4, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 4, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 4, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 4, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 3, 5, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 4, 5, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 4, 5, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 3, 6, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 4, 6, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 4, 6, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 3, 7, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 4, 7, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 4, 7, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 2, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 2, 3, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 2, 3, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 2, 3, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 2, 3, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 2, 3, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 4, 3, 3, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 2, 4, 4, 5, 3, 3, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 5, 3, 3, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 2, 4, 4, 6, 3, 3, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 6, 3, 3, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 2, 4, 3, 1, 0] + sm_links: *id020 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 3, 4, 3, 1, 0] + sm_links: *id020 diff --git a/tests/generators/fork_choice_generated/small/block_tree_tree_2.yaml b/tests/generators/fork_choice_generated/small/block_tree_tree_2.yaml new file mode 100644 index 0000000000..907dcd18fd --- /dev/null +++ b/tests/generators/fork_choice_generated/small/block_tree_tree_2.yaml @@ -0,0 +1,1252 @@ +- block_parents: &id002 [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1, 0] + sm_links: &id001 + - [0, 1] + - [0, 2] + - [0, 3] + - [0, 4] +- block_parents: &id003 [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 5, 4, 3, 2, 1, 0] + sm_links: *id001 +- block_parents: *id002 + sm_links: &id004 + - [0, 1] + - [0, 2] + - [0, 3] + - [0, 5] +- block_parents: *id003 + sm_links: *id004 +- block_parents: *id002 + sm_links: &id005 + - [0, 1] + - [0, 2] + - [0, 3] + - [1, 4] +- block_parents: *id003 + sm_links: *id005 +- block_parents: *id002 + sm_links: &id006 + - [0, 1] + - [0, 2] + - [0, 3] + - [2, 4] +- block_parents: *id003 + sm_links: *id006 +- block_parents: *id002 + sm_links: &id007 + - [0, 1] + - [0, 2] + - [0, 3] + - [3, 4] +- block_parents: *id003 + sm_links: *id007 +- block_parents: *id002 + sm_links: &id008 + - [0, 1] + - [0, 2] + - [0, 3] + - [1, 5] +- block_parents: *id003 + sm_links: *id008 +- block_parents: *id002 + sm_links: &id009 + - [0, 1] + - [0, 2] + - [0, 3] + - [2, 5] +- block_parents: *id003 + sm_links: *id009 +- block_parents: *id002 + sm_links: &id010 + - [0, 1] + - [0, 2] + - [0, 3] + - [3, 5] +- block_parents: *id003 + sm_links: *id010 +- block_parents: *id002 + sm_links: &id011 + - [0, 1] + - [0, 2] + - [1, 3] + - [1, 4] +- block_parents: *id003 + sm_links: *id011 +- block_parents: *id002 + sm_links: &id012 + - [0, 1] + - [0, 2] + - [1, 3] + - [2, 4] +- block_parents: *id003 + sm_links: *id012 +- block_parents: *id002 + sm_links: &id013 + - [0, 1] + - [0, 2] + - [1, 3] + - [3, 4] +- block_parents: *id003 + sm_links: *id013 +- block_parents: *id002 + sm_links: &id014 + - [0, 1] + - [0, 2] + - [1, 3] + - [1, 5] +- block_parents: *id003 + sm_links: *id014 +- block_parents: *id002 + sm_links: &id015 + - [0, 1] + - [0, 2] + - [1, 3] + - [2, 5] +- block_parents: *id003 + sm_links: *id015 +- block_parents: *id002 + sm_links: &id016 + - [0, 1] + - [0, 2] + - [1, 3] + - [3, 5] +- block_parents: *id003 + sm_links: *id016 +- block_parents: *id002 + sm_links: &id017 + - [0, 1] + - [0, 2] + - [2, 3] + - [2, 4] +- block_parents: *id003 + sm_links: *id017 +- block_parents: *id002 + sm_links: &id018 + - [0, 1] + - [0, 2] + - [2, 3] + - [3, 4] +- block_parents: *id003 + sm_links: *id018 +- block_parents: *id002 + sm_links: &id019 + - [0, 1] + - [0, 2] + - [2, 3] + - [2, 5] +- block_parents: *id003 + sm_links: *id019 +- block_parents: *id002 + sm_links: &id020 + - [0, 1] + - [0, 2] + - [2, 3] + - [3, 5] +- block_parents: *id003 + sm_links: *id020 +- block_parents: *id002 + sm_links: &id021 + - [0, 1] + - [0, 2] + - [0, 4] + - [0, 5] +- block_parents: *id003 + sm_links: *id021 +- block_parents: *id002 + sm_links: &id022 + - [0, 1] + - [0, 2] + - [0, 4] + - [1, 5] +- block_parents: *id003 + sm_links: *id022 +- block_parents: *id002 + sm_links: &id023 + - [0, 1] + - [0, 2] + - [0, 4] + - [2, 5] +- block_parents: *id003 + sm_links: *id023 +- block_parents: *id002 + sm_links: &id024 + - [0, 1] + - [0, 2] + - [0, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id024 +- block_parents: *id002 + sm_links: &id025 + - [0, 1] + - [0, 2] + - [1, 4] + - [1, 5] +- block_parents: *id003 + sm_links: *id025 +- block_parents: *id002 + sm_links: &id026 + - [0, 1] + - [0, 2] + - [1, 4] + - [2, 5] +- block_parents: *id003 + sm_links: *id026 +- block_parents: *id002 + sm_links: &id027 + - [0, 1] + - [0, 2] + - [1, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id027 +- block_parents: *id002 + sm_links: &id028 + - [0, 1] + - [0, 2] + - [2, 4] + - [2, 5] +- block_parents: *id003 + sm_links: *id028 +- block_parents: *id002 + sm_links: &id029 + - [0, 1] + - [0, 2] + - [2, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id029 +- block_parents: *id002 + sm_links: &id030 + - [0, 1] + - [0, 3] + - [0, 4] + - [0, 5] +- block_parents: *id003 + sm_links: *id030 +- block_parents: *id002 + sm_links: &id031 + - [0, 2] + - [0, 3] + - [0, 4] + - [0, 5] +- block_parents: *id003 + sm_links: *id031 +- block_parents: *id002 + sm_links: &id032 + - [0, 1] + - [0, 3] + - [0, 4] + - [1, 5] +- block_parents: *id003 + sm_links: *id032 +- block_parents: *id002 + sm_links: &id033 + - [0, 1] + - [0, 3] + - [0, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id033 +- block_parents: *id002 + sm_links: &id034 + - [0, 1] + - [0, 3] + - [0, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id034 +- block_parents: *id002 + sm_links: &id035 + - [0, 2] + - [0, 3] + - [0, 4] + - [2, 5] +- block_parents: *id003 + sm_links: *id035 +- block_parents: *id002 + sm_links: &id036 + - [0, 2] + - [0, 3] + - [0, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id036 +- block_parents: *id002 + sm_links: &id037 + - [0, 2] + - [0, 3] + - [0, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id037 +- block_parents: *id002 + sm_links: &id038 + - [0, 1] + - [0, 3] + - [1, 4] + - [1, 5] +- block_parents: *id003 + sm_links: *id038 +- block_parents: *id002 + sm_links: &id039 + - [0, 1] + - [0, 3] + - [1, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id039 +- block_parents: *id002 + sm_links: &id040 + - [0, 1] + - [0, 3] + - [1, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id040 +- block_parents: *id002 + sm_links: &id041 + - [0, 1] + - [0, 3] + - [3, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id041 +- block_parents: *id002 + sm_links: &id042 + - [0, 1] + - [0, 3] + - [3, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id042 +- block_parents: *id002 + sm_links: &id043 + - [0, 2] + - [0, 3] + - [2, 4] + - [2, 5] +- block_parents: *id003 + sm_links: *id043 +- block_parents: *id002 + sm_links: &id044 + - [0, 2] + - [0, 3] + - [2, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id044 +- block_parents: *id002 + sm_links: &id045 + - [0, 2] + - [0, 3] + - [2, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id045 +- block_parents: *id002 + sm_links: &id046 + - [0, 2] + - [0, 3] + - [3, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id046 +- block_parents: *id002 + sm_links: &id047 + - [0, 2] + - [0, 3] + - [3, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id047 +- block_parents: *id002 + sm_links: &id048 + - [0, 1] + - [1, 3] + - [1, 4] + - [1, 5] +- block_parents: *id003 + sm_links: *id048 +- block_parents: *id002 + sm_links: &id049 + - [0, 1] + - [1, 3] + - [1, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id049 +- block_parents: *id002 + sm_links: &id050 + - [0, 1] + - [1, 3] + - [1, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id050 +- block_parents: *id002 + sm_links: &id051 + - [0, 1] + - [1, 3] + - [3, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id051 +- block_parents: *id002 + sm_links: &id052 + - [0, 1] + - [1, 3] + - [3, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id052 +- block_parents: *id002 + sm_links: &id053 + - [0, 2] + - [2, 3] + - [2, 4] + - [2, 5] +- block_parents: *id003 + sm_links: *id053 +- block_parents: *id002 + sm_links: &id054 + - [0, 2] + - [2, 3] + - [2, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id054 +- block_parents: *id002 + sm_links: &id055 + - [0, 2] + - [2, 3] + - [2, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id055 +- block_parents: *id002 + sm_links: &id056 + - [0, 2] + - [2, 3] + - [3, 4] + - [3, 5] +- block_parents: *id003 + sm_links: *id056 +- block_parents: *id002 + sm_links: &id057 + - [0, 2] + - [2, 3] + - [3, 4] + - [4, 5] +- block_parents: *id003 + sm_links: *id057 +- block_parents: [0, 0, 1, 0, 0, 0] + sm_links: &id058 + - [0, 1] + - [0, 2] + - [2, 3] + - [3, 4] +- block_parents: [0, 0, 0, 1, 0, 0] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 0, 0] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 0, 0] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 0, 0] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 1, 0] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 1, 0] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 1, 0] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 1, 0] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 1, 0] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 1, 0] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 2, 0] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 2, 0] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 2, 0] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 2, 0] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 2, 0] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 2, 0] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 3, 0] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 3, 0] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 3, 0] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 3, 0] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 3, 0] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 3, 0] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 0, 1] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 0, 1] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 0, 1] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 0, 1] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 0, 1] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 0, 1] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 1, 1] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 1, 1] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 1, 1] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 1, 1] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 1, 1] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 1, 1] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 2, 1] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 2, 1] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 2, 1] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 2, 1] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 2, 1] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 2, 1] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 3, 1] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 3, 1] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 3, 1] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 3, 1] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 3, 1] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 3, 1] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 0, 2] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 0, 2] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 0, 2] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 0, 2] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 0, 2] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 0, 2] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 1, 2] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 1, 2] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 1, 2] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 1, 2] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 1, 2] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 1, 2] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 2, 2] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 2, 2] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 2, 2] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 2, 2] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 2, 2] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 2, 2] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 3, 2] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 3, 2] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 3, 2] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 3, 2] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 3, 2] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 3, 2] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 0, 3] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 0, 3] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 0, 3] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 0, 3] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 0, 3] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 0, 3] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 1, 3] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 1, 3] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 1, 3] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 1, 3] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 1, 3] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 1, 3] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 2, 3] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 2, 3] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 2, 3] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 2, 3] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 2, 3] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 2, 3] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 3, 3] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 3, 3] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 3, 3] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 3, 3] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 3, 3] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 3, 3] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 0, 4] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 0, 4] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 0, 4] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 0, 4] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 0, 4] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 0, 4] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 1, 4] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 1, 4] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 1, 4] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 1, 4] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 1, 4] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 1, 4] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 2, 4] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 2, 4] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 2, 4] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 2, 4] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 2, 4] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 2, 4] + sm_links: *id058 +- block_parents: [0, 0, 0, 0, 3, 4] + sm_links: *id058 +- block_parents: [0, 0, 1, 0, 3, 4] + sm_links: *id058 +- block_parents: [0, 0, 0, 1, 3, 4] + sm_links: *id058 +- block_parents: [0, 0, 1, 1, 3, 4] + sm_links: *id058 +- block_parents: [0, 0, 0, 2, 3, 4] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 3, 4] + sm_links: *id058 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0] + sm_links: &id059 + - [0, 1] + - [0, 2] + - [2, 3] + - [3, 4] +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 3, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 3, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 4, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 4, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 4, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 4, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 4, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 4, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 3, 5, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 4, 5, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 4, 5, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 3, 6, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 4, 6, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 4, 6, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 3, 7, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 4, 7, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 4, 7, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 2, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 2, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 2, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 2, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 2, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 2, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 4, 3, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 4, 5, 3, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 5, 3, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 4, 6, 3, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 6, 3, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 2, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 3, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 4, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 4, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 2, 5, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 3, 5, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 4, 5, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 4, 5, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 5, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 5, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 5, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 2, 6, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 3, 6, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 4, 6, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 4, 6, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 6, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 6, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 6, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 2, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 2, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 4, 3, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 3, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 2, 4, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 3, 4, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 4, 4, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 4, 4, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 4, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 4, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 4, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 2, 5, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 3, 5, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 4, 5, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 4, 5, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 4, 5, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 2, 6, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 3, 6, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 4, 6, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 4, 6, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 4, 6, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 6, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 6, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 6, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 2, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 2, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 4, 3, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 3, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 2, 4, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 3, 4, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 4, 4, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 4, 4, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 4, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 4, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 4, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 2, 5, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 3, 5, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 4, 5, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 4, 5, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 4, 5, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 5, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 5, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 5, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 2, 6, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 3, 6, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 4, 6, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 4, 6, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 4, 6, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 6, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 6, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 6, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 2, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 2, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 4, 3, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 3, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 2, 4, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 3, 4, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 4, 4, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 4, 4, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 4, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 4, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 4, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 2, 5, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 3, 5, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 4, 5, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 4, 5, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 4, 5, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 5, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 5, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 5, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 2, 6, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 3, 6, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 4, 6, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 4, 6, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 4, 6, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 6, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 6, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 6, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 3, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 2, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 2, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 2, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 2, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 2, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 2, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 2, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 2, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 2, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 2, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 2, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 3, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 4, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 4, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 2, 5, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 3, 5, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 4, 5, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 4, 5, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 5, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 5, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 5, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 2, 6, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 3, 6, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 4, 6, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 4, 6, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 6, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 6, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 6, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 2, 4, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 3, 4, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 3, 4, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 2, 5, 4, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 3, 5, 4, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 3, 5, 4, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 5, 4, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 5, 4, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 2, 6, 4, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 3, 6, 4, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 3, 6, 4, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 6, 4, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 6, 4, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 2, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 2, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 2, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 2, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 2, 3, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 3, 3, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 4, 3, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 4, 3, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 3, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 3, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 3, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 2, 4, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 3, 4, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 3, 4, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 4, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 4, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 2, 5, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 2, 5, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 3, 5, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 3, 5, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 3, 5, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 4, 5, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 4, 5, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 2, 6, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 2, 6, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 3, 6, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 3, 6, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 3, 6, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 4, 6, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 4, 6, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 6, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 6, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 6, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 3, 2, 6, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 4, 2, 6, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 5, 2, 6, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 2, 6, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 2, 3, 6, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 3, 3, 6, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 4, 3, 6, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 4, 3, 6, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 5, 3, 6, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 3, 5, 3, 6, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 2, 4, 5, 3, 6, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 3, 2, 4, 6, 4, 1, 0] + sm_links: *id059 +- block_parents: [0, 0, 1, 2, 3, 2, 3, 4, 6, 4, 1, 0] + sm_links: *id059 diff --git a/tests/generators/fork_choice_generated/small/test_gen.yaml b/tests/generators/fork_choice_generated/small/test_gen.yaml index d14ed5cd54..2deea7b017 100644 --- a/tests/generators/fork_choice_generated/small/test_gen.yaml +++ b/tests/generators/fork_choice_generated/small/test_gen.yaml @@ -1,32 +1,38 @@ block_tree_test: test_type: block_tree - instances: small/block_tree_tree.yaml # 12 + instances: smoke/block_tree_tree.yaml # 128 seed: 123 nr_variations: 2 nr_mutations: 1 block_weight_test: test_type: block_tree - instances: small/block_tree_other.yaml # 3 + instances: smoke/block_tree_other.yaml # 4 seed: 123 - nr_variations: 5 - nr_mutations: 0 -attester_slashing_test: + nr_variations: 32 + nr_mutations: 1 +shuffling_test: test_type: block_tree - instances: small/block_tree_other.yaml # 3 + instances: smoke/block_tree_other.yaml # 4 seed: 123 nr_variations: 2 - nr_mutations: 1 + nr_mutations: 31 +attester_slashing_test: + test_type: block_tree + instances: smoke/block_tree_other.yaml # 4 + seed: 123 + nr_variations: 8 + nr_mutations: 3 with_attester_slashings: true invalid_message_test: test_type: block_tree - instances: small/block_tree_other.yaml # 3 + instances: smoke/block_tree_other.yaml # 4 seed: 123 - nr_variations: 2 + nr_variations: 16 nr_mutations: 1 with_invalid_messages: true block_cover_test: test_type: block_cover - instances: small/block_cover.yaml # 12 + instances: smoke/block_cover.yaml # 24 seed: 456 nr_variations: 2 - nr_mutations: 1 + nr_mutations: 3 diff --git a/tests/generators/fork_choice_generated/tiny/block_cover.yaml b/tests/generators/fork_choice_generated/tiny/block_cover.yaml new file mode 100644 index 0000000000..aa6d88f7d4 --- /dev/null +++ b/tests/generators/fork_choice_generated/tiny/block_cover.yaml @@ -0,0 +1,108 @@ +- block_epochs: [0] + current_epoch: 1 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: true} + previous_justifications: [false] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [0, 1] + current_epoch: 1 + current_justifications: [false, false] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: true} + previous_justifications: [false, false] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [0] + current_epoch: 3 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: true} + previous_justifications: [false] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [0, 1] + current_epoch: 3 + current_justifications: [false, false] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: true} + previous_justifications: [false, false] + store_justified_epoch: 0 + target_block: 0 +- block_epochs: [2] + current_epoch: 3 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2, 3] + current_epoch: 3 + current_justifications: [false, false] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2] + current_epoch: 5 + current_justifications: [false] + parents: [0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2, 3] + current_epoch: 5 + current_justifications: [false, false] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, false] + store_justified_epoch: 2 + target_block: 0 +- block_epochs: [2, 3, 3] + current_epoch: 4 + current_justifications: [false, false, true] + parents: [0, 0, 0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false, false] + store_justified_epoch: 3 + target_block: 1 +- block_epochs: [2, 3] + current_epoch: 4 + current_justifications: [false, true] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: true, + store_je_eq_zero: false} + previous_justifications: [false, false] + store_justified_epoch: 3 + target_block: 0 +- block_epochs: [2, 3, 3] + current_epoch: 5 + current_justifications: [false, false, true] + parents: [0, 0, 0] + predicates: {block_is_leaf: true, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, false, false] + store_justified_epoch: 3 + target_block: 1 +- block_epochs: [2, 3] + current_epoch: 5 + current_justifications: [false, true] + parents: [0, 0] + predicates: {block_is_leaf: false, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: false, + store_je_eq_zero: false} + previous_justifications: [false, false] + store_justified_epoch: 3 + target_block: 0 diff --git a/tests/generators/fork_choice_generated/tiny/block_tree_other.yaml b/tests/generators/fork_choice_generated/tiny/block_tree_other.yaml new file mode 100644 index 0000000000..edd147b557 --- /dev/null +++ b/tests/generators/fork_choice_generated/tiny/block_tree_other.yaml @@ -0,0 +1,10 @@ +- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0] + sm_links: &id001 + - [0, 1] + - [0, 2] + - [2, 3] + - [3, 4] +- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 3, 2, 1, 0] + sm_links: *id001 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 2, 1, 0] + sm_links: *id001 diff --git a/tests/generators/fork_choice_generated/tiny/block_tree_tree.yaml b/tests/generators/fork_choice_generated/tiny/block_tree_tree.yaml new file mode 100644 index 0000000000..ca4f470ac8 --- /dev/null +++ b/tests/generators/fork_choice_generated/tiny/block_tree_tree.yaml @@ -0,0 +1,37 @@ +- block_parents: &id002 [0, 0, 1, 2, 3, 2, 1, 0] + sm_links: &id001 + - [0, 1] + - [0, 2] + - [0, 3] +- block_parents: &id003 [0, 0, 1, 2, 2, 3, 1, 0] + sm_links: *id001 +- block_parents: &id005 [0, 0, 1, 2, 3, 3, 1, 0] + sm_links: *id001 +- block_parents: *id002 + sm_links: &id004 + - [0, 1] + - [0, 2] + - [1, 3] +- block_parents: *id003 + sm_links: *id004 +- block_parents: *id005 + sm_links: *id004 +- block_parents: *id002 + sm_links: &id006 + - [0, 1] + - [0, 2] + - [2, 3] +- block_parents: *id003 + sm_links: *id006 +- block_parents: *id005 + sm_links: *id006 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1, 0] + sm_links: &id007 + - [0, 1] + - [0, 2] + - [2, 3] + - [3, 4] +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 5, 4, 3, 2, 1, 0] + sm_links: *id007 +- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 5, 4, 3, 2, 1, 0] + sm_links: *id007 diff --git a/tests/generators/fork_choice_generated/tiny/test_gen.yaml b/tests/generators/fork_choice_generated/tiny/test_gen.yaml new file mode 100644 index 0000000000..d14ed5cd54 --- /dev/null +++ b/tests/generators/fork_choice_generated/tiny/test_gen.yaml @@ -0,0 +1,32 @@ +block_tree_test: + test_type: block_tree + instances: small/block_tree_tree.yaml # 12 + seed: 123 + nr_variations: 2 + nr_mutations: 1 +block_weight_test: + test_type: block_tree + instances: small/block_tree_other.yaml # 3 + seed: 123 + nr_variations: 5 + nr_mutations: 0 +attester_slashing_test: + test_type: block_tree + instances: small/block_tree_other.yaml # 3 + seed: 123 + nr_variations: 2 + nr_mutations: 1 + with_attester_slashings: true +invalid_message_test: + test_type: block_tree + instances: small/block_tree_other.yaml # 3 + seed: 123 + nr_variations: 2 + nr_mutations: 1 + with_invalid_messages: true +block_cover_test: + test_type: block_cover + instances: small/block_cover.yaml # 12 + seed: 456 + nr_variations: 2 + nr_mutations: 1 From bd5e38b8456ab8f5a29881f0e7a972a6ab520973 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 11 Jun 2024 20:46:30 +0400 Subject: [PATCH 081/111] renamed small -> tiny, smoke -> small --- .../smoke/block_cover.yaml | 216 --- .../smoke/block_tree_other.yaml | 12 - .../smoke/block_tree_tree.yaml | 312 ---- .../smoke/block_tree_tree_2.yaml | 1252 ----------------- .../fork_choice_generated/smoke/test_gen.yaml | 38 - 5 files changed, 1830 deletions(-) delete mode 100644 tests/generators/fork_choice_generated/smoke/block_cover.yaml delete mode 100644 tests/generators/fork_choice_generated/smoke/block_tree_other.yaml delete mode 100644 tests/generators/fork_choice_generated/smoke/block_tree_tree.yaml delete mode 100644 tests/generators/fork_choice_generated/smoke/block_tree_tree_2.yaml delete mode 100644 tests/generators/fork_choice_generated/smoke/test_gen.yaml diff --git a/tests/generators/fork_choice_generated/smoke/block_cover.yaml b/tests/generators/fork_choice_generated/smoke/block_cover.yaml deleted file mode 100644 index 6f8ad60ce6..0000000000 --- a/tests/generators/fork_choice_generated/smoke/block_cover.yaml +++ /dev/null @@ -1,216 +0,0 @@ -- block_epochs: [0] - current_epoch: 1 - current_justifications: [false] - parents: [0] - predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, - store_je_eq_zero: true} - previous_justifications: [false] - store_justified_epoch: 0 - target_block: 0 -- block_epochs: [0] - current_epoch: 1 - current_justifications: [false] - parents: [0] - predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, - store_je_eq_zero: true} - previous_justifications: [false] - store_justified_epoch: 0 - target_block: 0 -- block_epochs: [0, 1] - current_epoch: 1 - current_justifications: [false, false] - parents: [0, 0] - predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, - store_je_eq_zero: true} - previous_justifications: [false, false] - store_justified_epoch: 0 - target_block: 0 -- block_epochs: [0, 1, 1] - current_epoch: 1 - current_justifications: [false, false, false] - parents: [0, 0, 0] - predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, - store_je_eq_zero: true} - previous_justifications: [false, false, false] - store_justified_epoch: 0 - target_block: 0 -- block_epochs: [0] - current_epoch: 3 - current_justifications: [false] - parents: [0] - predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, - store_je_eq_zero: true} - previous_justifications: [false] - store_justified_epoch: 0 - target_block: 0 -- block_epochs: [0] - current_epoch: 3 - current_justifications: [false] - parents: [0] - predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, - store_je_eq_zero: true} - previous_justifications: [false] - store_justified_epoch: 0 - target_block: 0 -- block_epochs: [0, 1] - current_epoch: 3 - current_justifications: [false, false] - parents: [0, 0] - predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, - store_je_eq_zero: true} - previous_justifications: [false, false] - store_justified_epoch: 0 - target_block: 0 -- block_epochs: [0, 1] - current_epoch: 3 - current_justifications: [false, false] - parents: [0, 0] - predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, - store_je_eq_zero: true} - previous_justifications: [false, true] - store_justified_epoch: 0 - target_block: 0 -- block_epochs: [2] - current_epoch: 3 - current_justifications: [false] - parents: [0] - predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, - store_je_eq_zero: false} - previous_justifications: [false] - store_justified_epoch: 2 - target_block: 0 -- block_epochs: [2] - current_epoch: 3 - current_justifications: [false] - parents: [0] - predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, - store_je_eq_zero: false} - previous_justifications: [false] - store_justified_epoch: 2 - target_block: 0 -- block_epochs: [2, 3] - current_epoch: 3 - current_justifications: [false, false] - parents: [0, 0] - predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, - store_je_eq_zero: false} - previous_justifications: [false, false] - store_justified_epoch: 2 - target_block: 0 -- block_epochs: [2, 3, 3] - current_epoch: 3 - current_justifications: [false, false, false] - parents: [0, 0, 0] - predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: true, - store_je_eq_zero: false} - previous_justifications: [false, false, false] - store_justified_epoch: 2 - target_block: 0 -- block_epochs: [2] - current_epoch: 5 - current_justifications: [false] - parents: [0] - predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, - store_je_eq_zero: false} - previous_justifications: [false] - store_justified_epoch: 2 - target_block: 0 -- block_epochs: [2] - current_epoch: 5 - current_justifications: [false] - parents: [0] - predicates: {block_is_leaf: true, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, - store_je_eq_zero: false} - previous_justifications: [false] - store_justified_epoch: 2 - target_block: 0 -- block_epochs: [2, 3] - current_epoch: 5 - current_justifications: [false, false] - parents: [0, 0] - predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, - store_je_eq_zero: false} - previous_justifications: [false, false] - store_justified_epoch: 2 - target_block: 0 -- block_epochs: [2, 3, 3] - current_epoch: 5 - current_justifications: [false, false, false] - parents: [0, 0, 0] - predicates: {block_is_leaf: false, block_vse_eq_store_je: true, block_vse_plus_two_ge_curr_e: false, - store_je_eq_zero: false} - previous_justifications: [false, false, false] - store_justified_epoch: 2 - target_block: 0 -- block_epochs: [2, 3, 3] - current_epoch: 4 - current_justifications: [false, false, true] - parents: [0, 0, 0] - predicates: {block_is_leaf: true, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: true, - store_je_eq_zero: false} - previous_justifications: [false, false, false] - store_justified_epoch: 3 - target_block: 1 -- block_epochs: [2, 3, 3] - current_epoch: 4 - current_justifications: [false, false, true] - parents: [0, 0, 0] - predicates: {block_is_leaf: true, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: true, - store_je_eq_zero: false} - previous_justifications: [false, false, true] - store_justified_epoch: 3 - target_block: 1 -- block_epochs: [2, 3] - current_epoch: 4 - current_justifications: [false, true] - parents: [0, 0] - predicates: {block_is_leaf: false, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: true, - store_je_eq_zero: false} - previous_justifications: [false, false] - store_justified_epoch: 3 - target_block: 0 -- block_epochs: [2, 3] - current_epoch: 4 - current_justifications: [false, true] - parents: [0, 0] - predicates: {block_is_leaf: false, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: true, - store_je_eq_zero: false} - previous_justifications: [false, true] - store_justified_epoch: 3 - target_block: 0 -- block_epochs: [2, 3, 3] - current_epoch: 5 - current_justifications: [false, false, true] - parents: [0, 0, 0] - predicates: {block_is_leaf: true, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: false, - store_je_eq_zero: false} - previous_justifications: [false, false, false] - store_justified_epoch: 3 - target_block: 1 -- block_epochs: [2, 3, 3] - current_epoch: 5 - current_justifications: [false, false, true] - parents: [0, 0, 0] - predicates: {block_is_leaf: true, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: false, - store_je_eq_zero: false} - previous_justifications: [false, false, true] - store_justified_epoch: 3 - target_block: 1 -- block_epochs: [2, 3] - current_epoch: 5 - current_justifications: [false, true] - parents: [0, 0] - predicates: {block_is_leaf: false, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: false, - store_je_eq_zero: false} - previous_justifications: [false, false] - store_justified_epoch: 3 - target_block: 0 -- block_epochs: [2, 3] - current_epoch: 5 - current_justifications: [false, true] - parents: [0, 0] - predicates: {block_is_leaf: false, block_vse_eq_store_je: false, block_vse_plus_two_ge_curr_e: false, - store_je_eq_zero: false} - previous_justifications: [false, true] - store_justified_epoch: 3 - target_block: 0 diff --git a/tests/generators/fork_choice_generated/smoke/block_tree_other.yaml b/tests/generators/fork_choice_generated/smoke/block_tree_other.yaml deleted file mode 100644 index e39c4ebc06..0000000000 --- a/tests/generators/fork_choice_generated/smoke/block_tree_other.yaml +++ /dev/null @@ -1,12 +0,0 @@ -- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0] - sm_links: &id001 - - [0, 1] - - [0, 2] - - [2, 3] - - [3, 4] -- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 3, 2, 1, 0] - sm_links: *id001 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 2, 1, 0] - sm_links: *id001 -- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 3, 2, 1, 0] - sm_links: *id001 diff --git a/tests/generators/fork_choice_generated/smoke/block_tree_tree.yaml b/tests/generators/fork_choice_generated/smoke/block_tree_tree.yaml deleted file mode 100644 index a2a58b7eae..0000000000 --- a/tests/generators/fork_choice_generated/smoke/block_tree_tree.yaml +++ /dev/null @@ -1,312 +0,0 @@ -- block_parents: &id002 [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1, 0] - sm_links: &id001 - - [0, 1] - - [0, 2] - - [0, 3] -- block_parents: &id003 [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 5, 4, 3, 2, 1, 0] - sm_links: *id001 -- block_parents: *id002 - sm_links: &id004 - - [0, 1] - - [0, 2] - - [0, 4] -- block_parents: *id003 - sm_links: *id004 -- block_parents: *id002 - sm_links: &id005 - - [0, 1] - - [0, 2] - - [1, 3] -- block_parents: *id003 - sm_links: *id005 -- block_parents: *id002 - sm_links: &id006 - - [0, 1] - - [0, 2] - - [2, 3] -- block_parents: *id003 - sm_links: *id006 -- block_parents: *id002 - sm_links: &id007 - - [0, 1] - - [0, 2] - - [1, 4] -- block_parents: *id003 - sm_links: *id007 -- block_parents: *id002 - sm_links: &id008 - - [0, 1] - - [0, 2] - - [2, 4] -- block_parents: *id003 - sm_links: *id008 -- block_parents: *id002 - sm_links: &id009 - - [0, 1] - - [0, 3] - - [0, 4] -- block_parents: *id003 - sm_links: *id009 -- block_parents: *id002 - sm_links: &id010 - - [0, 2] - - [0, 3] - - [0, 4] -- block_parents: *id003 - sm_links: *id010 -- block_parents: *id002 - sm_links: &id011 - - [0, 1] - - [0, 3] - - [1, 4] -- block_parents: *id003 - sm_links: *id011 -- block_parents: *id002 - sm_links: &id012 - - [0, 1] - - [0, 3] - - [3, 4] -- block_parents: *id003 - sm_links: *id012 -- block_parents: *id002 - sm_links: &id013 - - [0, 2] - - [0, 3] - - [2, 4] -- block_parents: *id003 - sm_links: *id013 -- block_parents: *id002 - sm_links: &id014 - - [0, 2] - - [0, 3] - - [3, 4] -- block_parents: *id003 - sm_links: *id014 -- block_parents: *id002 - sm_links: &id015 - - [0, 1] - - [1, 3] - - [1, 4] -- block_parents: *id003 - sm_links: *id015 -- block_parents: *id002 - sm_links: &id016 - - [0, 1] - - [1, 3] - - [3, 4] -- block_parents: *id003 - sm_links: *id016 -- block_parents: *id002 - sm_links: &id017 - - [0, 2] - - [2, 3] - - [2, 4] -- block_parents: *id003 - sm_links: *id017 -- block_parents: *id002 - sm_links: &id018 - - [0, 2] - - [2, 3] - - [3, 4] -- block_parents: *id003 - sm_links: *id018 -- block_parents: [0, 0, 1, 0, 0] - sm_links: &id019 - - [0, 1] - - [0, 2] - - [2, 3] - - [3, 4] -- block_parents: [0, 0, 0, 1, 0] - sm_links: *id019 -- block_parents: [0, 0, 1, 1, 0] - sm_links: *id019 -- block_parents: [0, 0, 0, 2, 0] - sm_links: *id019 -- block_parents: [0, 0, 1, 2, 0] - sm_links: *id019 -- block_parents: [0, 0, 0, 0, 1] - sm_links: *id019 -- block_parents: [0, 0, 1, 0, 1] - sm_links: *id019 -- block_parents: [0, 0, 0, 1, 1] - sm_links: *id019 -- block_parents: [0, 0, 1, 1, 1] - sm_links: *id019 -- block_parents: [0, 0, 0, 2, 1] - sm_links: *id019 -- block_parents: [0, 0, 1, 2, 1] - sm_links: *id019 -- block_parents: [0, 0, 0, 0, 2] - sm_links: *id019 -- block_parents: [0, 0, 1, 0, 2] - sm_links: *id019 -- block_parents: [0, 0, 0, 1, 2] - sm_links: *id019 -- block_parents: [0, 0, 1, 1, 2] - sm_links: *id019 -- block_parents: [0, 0, 0, 2, 2] - sm_links: *id019 -- block_parents: [0, 0, 1, 2, 2] - sm_links: *id019 -- block_parents: [0, 0, 0, 0, 3] - sm_links: *id019 -- block_parents: [0, 0, 1, 0, 3] - sm_links: *id019 -- block_parents: [0, 0, 0, 1, 3] - sm_links: *id019 -- block_parents: [0, 0, 1, 1, 3] - sm_links: *id019 -- block_parents: [0, 0, 0, 2, 3] - sm_links: *id019 -- block_parents: [0, 0, 1, 2, 3] - sm_links: *id019 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0] - sm_links: &id020 - - [0, 1] - - [0, 2] - - [2, 3] - - [3, 4] -- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 3, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 3, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 4, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 4, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 4, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 4, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 4, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 4, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 4, 3, 5, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 3, 4, 5, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 3, 4, 4, 5, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 4, 3, 6, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 3, 4, 6, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 3, 4, 4, 6, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 4, 3, 7, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 3, 4, 7, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 3, 4, 4, 7, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 2, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 2, 3, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 2, 3, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 2, 3, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 2, 3, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 2, 3, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 2, 4, 5, 4, 3, 3, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 2, 4, 4, 5, 3, 3, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 2, 4, 5, 5, 3, 3, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 2, 4, 4, 6, 3, 3, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 2, 4, 5, 6, 3, 3, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 2, 4, 3, 1, 0] - sm_links: *id020 -- block_parents: [0, 0, 1, 2, 2, 4, 5, 3, 4, 3, 1, 0] - sm_links: *id020 diff --git a/tests/generators/fork_choice_generated/smoke/block_tree_tree_2.yaml b/tests/generators/fork_choice_generated/smoke/block_tree_tree_2.yaml deleted file mode 100644 index 907dcd18fd..0000000000 --- a/tests/generators/fork_choice_generated/smoke/block_tree_tree_2.yaml +++ /dev/null @@ -1,1252 +0,0 @@ -- block_parents: &id002 [0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1, 0] - sm_links: &id001 - - [0, 1] - - [0, 2] - - [0, 3] - - [0, 4] -- block_parents: &id003 [0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 5, 4, 3, 2, 1, 0] - sm_links: *id001 -- block_parents: *id002 - sm_links: &id004 - - [0, 1] - - [0, 2] - - [0, 3] - - [0, 5] -- block_parents: *id003 - sm_links: *id004 -- block_parents: *id002 - sm_links: &id005 - - [0, 1] - - [0, 2] - - [0, 3] - - [1, 4] -- block_parents: *id003 - sm_links: *id005 -- block_parents: *id002 - sm_links: &id006 - - [0, 1] - - [0, 2] - - [0, 3] - - [2, 4] -- block_parents: *id003 - sm_links: *id006 -- block_parents: *id002 - sm_links: &id007 - - [0, 1] - - [0, 2] - - [0, 3] - - [3, 4] -- block_parents: *id003 - sm_links: *id007 -- block_parents: *id002 - sm_links: &id008 - - [0, 1] - - [0, 2] - - [0, 3] - - [1, 5] -- block_parents: *id003 - sm_links: *id008 -- block_parents: *id002 - sm_links: &id009 - - [0, 1] - - [0, 2] - - [0, 3] - - [2, 5] -- block_parents: *id003 - sm_links: *id009 -- block_parents: *id002 - sm_links: &id010 - - [0, 1] - - [0, 2] - - [0, 3] - - [3, 5] -- block_parents: *id003 - sm_links: *id010 -- block_parents: *id002 - sm_links: &id011 - - [0, 1] - - [0, 2] - - [1, 3] - - [1, 4] -- block_parents: *id003 - sm_links: *id011 -- block_parents: *id002 - sm_links: &id012 - - [0, 1] - - [0, 2] - - [1, 3] - - [2, 4] -- block_parents: *id003 - sm_links: *id012 -- block_parents: *id002 - sm_links: &id013 - - [0, 1] - - [0, 2] - - [1, 3] - - [3, 4] -- block_parents: *id003 - sm_links: *id013 -- block_parents: *id002 - sm_links: &id014 - - [0, 1] - - [0, 2] - - [1, 3] - - [1, 5] -- block_parents: *id003 - sm_links: *id014 -- block_parents: *id002 - sm_links: &id015 - - [0, 1] - - [0, 2] - - [1, 3] - - [2, 5] -- block_parents: *id003 - sm_links: *id015 -- block_parents: *id002 - sm_links: &id016 - - [0, 1] - - [0, 2] - - [1, 3] - - [3, 5] -- block_parents: *id003 - sm_links: *id016 -- block_parents: *id002 - sm_links: &id017 - - [0, 1] - - [0, 2] - - [2, 3] - - [2, 4] -- block_parents: *id003 - sm_links: *id017 -- block_parents: *id002 - sm_links: &id018 - - [0, 1] - - [0, 2] - - [2, 3] - - [3, 4] -- block_parents: *id003 - sm_links: *id018 -- block_parents: *id002 - sm_links: &id019 - - [0, 1] - - [0, 2] - - [2, 3] - - [2, 5] -- block_parents: *id003 - sm_links: *id019 -- block_parents: *id002 - sm_links: &id020 - - [0, 1] - - [0, 2] - - [2, 3] - - [3, 5] -- block_parents: *id003 - sm_links: *id020 -- block_parents: *id002 - sm_links: &id021 - - [0, 1] - - [0, 2] - - [0, 4] - - [0, 5] -- block_parents: *id003 - sm_links: *id021 -- block_parents: *id002 - sm_links: &id022 - - [0, 1] - - [0, 2] - - [0, 4] - - [1, 5] -- block_parents: *id003 - sm_links: *id022 -- block_parents: *id002 - sm_links: &id023 - - [0, 1] - - [0, 2] - - [0, 4] - - [2, 5] -- block_parents: *id003 - sm_links: *id023 -- block_parents: *id002 - sm_links: &id024 - - [0, 1] - - [0, 2] - - [0, 4] - - [4, 5] -- block_parents: *id003 - sm_links: *id024 -- block_parents: *id002 - sm_links: &id025 - - [0, 1] - - [0, 2] - - [1, 4] - - [1, 5] -- block_parents: *id003 - sm_links: *id025 -- block_parents: *id002 - sm_links: &id026 - - [0, 1] - - [0, 2] - - [1, 4] - - [2, 5] -- block_parents: *id003 - sm_links: *id026 -- block_parents: *id002 - sm_links: &id027 - - [0, 1] - - [0, 2] - - [1, 4] - - [4, 5] -- block_parents: *id003 - sm_links: *id027 -- block_parents: *id002 - sm_links: &id028 - - [0, 1] - - [0, 2] - - [2, 4] - - [2, 5] -- block_parents: *id003 - sm_links: *id028 -- block_parents: *id002 - sm_links: &id029 - - [0, 1] - - [0, 2] - - [2, 4] - - [4, 5] -- block_parents: *id003 - sm_links: *id029 -- block_parents: *id002 - sm_links: &id030 - - [0, 1] - - [0, 3] - - [0, 4] - - [0, 5] -- block_parents: *id003 - sm_links: *id030 -- block_parents: *id002 - sm_links: &id031 - - [0, 2] - - [0, 3] - - [0, 4] - - [0, 5] -- block_parents: *id003 - sm_links: *id031 -- block_parents: *id002 - sm_links: &id032 - - [0, 1] - - [0, 3] - - [0, 4] - - [1, 5] -- block_parents: *id003 - sm_links: *id032 -- block_parents: *id002 - sm_links: &id033 - - [0, 1] - - [0, 3] - - [0, 4] - - [3, 5] -- block_parents: *id003 - sm_links: *id033 -- block_parents: *id002 - sm_links: &id034 - - [0, 1] - - [0, 3] - - [0, 4] - - [4, 5] -- block_parents: *id003 - sm_links: *id034 -- block_parents: *id002 - sm_links: &id035 - - [0, 2] - - [0, 3] - - [0, 4] - - [2, 5] -- block_parents: *id003 - sm_links: *id035 -- block_parents: *id002 - sm_links: &id036 - - [0, 2] - - [0, 3] - - [0, 4] - - [3, 5] -- block_parents: *id003 - sm_links: *id036 -- block_parents: *id002 - sm_links: &id037 - - [0, 2] - - [0, 3] - - [0, 4] - - [4, 5] -- block_parents: *id003 - sm_links: *id037 -- block_parents: *id002 - sm_links: &id038 - - [0, 1] - - [0, 3] - - [1, 4] - - [1, 5] -- block_parents: *id003 - sm_links: *id038 -- block_parents: *id002 - sm_links: &id039 - - [0, 1] - - [0, 3] - - [1, 4] - - [3, 5] -- block_parents: *id003 - sm_links: *id039 -- block_parents: *id002 - sm_links: &id040 - - [0, 1] - - [0, 3] - - [1, 4] - - [4, 5] -- block_parents: *id003 - sm_links: *id040 -- block_parents: *id002 - sm_links: &id041 - - [0, 1] - - [0, 3] - - [3, 4] - - [3, 5] -- block_parents: *id003 - sm_links: *id041 -- block_parents: *id002 - sm_links: &id042 - - [0, 1] - - [0, 3] - - [3, 4] - - [4, 5] -- block_parents: *id003 - sm_links: *id042 -- block_parents: *id002 - sm_links: &id043 - - [0, 2] - - [0, 3] - - [2, 4] - - [2, 5] -- block_parents: *id003 - sm_links: *id043 -- block_parents: *id002 - sm_links: &id044 - - [0, 2] - - [0, 3] - - [2, 4] - - [3, 5] -- block_parents: *id003 - sm_links: *id044 -- block_parents: *id002 - sm_links: &id045 - - [0, 2] - - [0, 3] - - [2, 4] - - [4, 5] -- block_parents: *id003 - sm_links: *id045 -- block_parents: *id002 - sm_links: &id046 - - [0, 2] - - [0, 3] - - [3, 4] - - [3, 5] -- block_parents: *id003 - sm_links: *id046 -- block_parents: *id002 - sm_links: &id047 - - [0, 2] - - [0, 3] - - [3, 4] - - [4, 5] -- block_parents: *id003 - sm_links: *id047 -- block_parents: *id002 - sm_links: &id048 - - [0, 1] - - [1, 3] - - [1, 4] - - [1, 5] -- block_parents: *id003 - sm_links: *id048 -- block_parents: *id002 - sm_links: &id049 - - [0, 1] - - [1, 3] - - [1, 4] - - [3, 5] -- block_parents: *id003 - sm_links: *id049 -- block_parents: *id002 - sm_links: &id050 - - [0, 1] - - [1, 3] - - [1, 4] - - [4, 5] -- block_parents: *id003 - sm_links: *id050 -- block_parents: *id002 - sm_links: &id051 - - [0, 1] - - [1, 3] - - [3, 4] - - [3, 5] -- block_parents: *id003 - sm_links: *id051 -- block_parents: *id002 - sm_links: &id052 - - [0, 1] - - [1, 3] - - [3, 4] - - [4, 5] -- block_parents: *id003 - sm_links: *id052 -- block_parents: *id002 - sm_links: &id053 - - [0, 2] - - [2, 3] - - [2, 4] - - [2, 5] -- block_parents: *id003 - sm_links: *id053 -- block_parents: *id002 - sm_links: &id054 - - [0, 2] - - [2, 3] - - [2, 4] - - [3, 5] -- block_parents: *id003 - sm_links: *id054 -- block_parents: *id002 - sm_links: &id055 - - [0, 2] - - [2, 3] - - [2, 4] - - [4, 5] -- block_parents: *id003 - sm_links: *id055 -- block_parents: *id002 - sm_links: &id056 - - [0, 2] - - [2, 3] - - [3, 4] - - [3, 5] -- block_parents: *id003 - sm_links: *id056 -- block_parents: *id002 - sm_links: &id057 - - [0, 2] - - [2, 3] - - [3, 4] - - [4, 5] -- block_parents: *id003 - sm_links: *id057 -- block_parents: [0, 0, 1, 0, 0, 0] - sm_links: &id058 - - [0, 1] - - [0, 2] - - [2, 3] - - [3, 4] -- block_parents: [0, 0, 0, 1, 0, 0] - sm_links: *id058 -- block_parents: [0, 0, 1, 1, 0, 0] - sm_links: *id058 -- block_parents: [0, 0, 0, 2, 0, 0] - sm_links: *id058 -- block_parents: [0, 0, 1, 2, 0, 0] - sm_links: *id058 -- block_parents: [0, 0, 0, 0, 1, 0] - sm_links: *id058 -- block_parents: [0, 0, 1, 0, 1, 0] - sm_links: *id058 -- block_parents: [0, 0, 0, 1, 1, 0] - sm_links: *id058 -- block_parents: [0, 0, 1, 1, 1, 0] - sm_links: *id058 -- block_parents: [0, 0, 0, 2, 1, 0] - sm_links: *id058 -- block_parents: [0, 0, 1, 2, 1, 0] - sm_links: *id058 -- block_parents: [0, 0, 0, 0, 2, 0] - sm_links: *id058 -- block_parents: [0, 0, 1, 0, 2, 0] - sm_links: *id058 -- block_parents: [0, 0, 0, 1, 2, 0] - sm_links: *id058 -- block_parents: [0, 0, 1, 1, 2, 0] - sm_links: *id058 -- block_parents: [0, 0, 0, 2, 2, 0] - sm_links: *id058 -- block_parents: [0, 0, 1, 2, 2, 0] - sm_links: *id058 -- block_parents: [0, 0, 0, 0, 3, 0] - sm_links: *id058 -- block_parents: [0, 0, 1, 0, 3, 0] - sm_links: *id058 -- block_parents: [0, 0, 0, 1, 3, 0] - sm_links: *id058 -- block_parents: [0, 0, 1, 1, 3, 0] - sm_links: *id058 -- block_parents: [0, 0, 0, 2, 3, 0] - sm_links: *id058 -- block_parents: [0, 0, 1, 2, 3, 0] - sm_links: *id058 -- block_parents: [0, 0, 0, 0, 0, 1] - sm_links: *id058 -- block_parents: [0, 0, 1, 0, 0, 1] - sm_links: *id058 -- block_parents: [0, 0, 0, 1, 0, 1] - sm_links: *id058 -- block_parents: [0, 0, 1, 1, 0, 1] - sm_links: *id058 -- block_parents: [0, 0, 0, 2, 0, 1] - sm_links: *id058 -- block_parents: [0, 0, 1, 2, 0, 1] - sm_links: *id058 -- block_parents: [0, 0, 0, 0, 1, 1] - sm_links: *id058 -- block_parents: [0, 0, 1, 0, 1, 1] - sm_links: *id058 -- block_parents: [0, 0, 0, 1, 1, 1] - sm_links: *id058 -- block_parents: [0, 0, 1, 1, 1, 1] - sm_links: *id058 -- block_parents: [0, 0, 0, 2, 1, 1] - sm_links: *id058 -- block_parents: [0, 0, 1, 2, 1, 1] - sm_links: *id058 -- block_parents: [0, 0, 0, 0, 2, 1] - sm_links: *id058 -- block_parents: [0, 0, 1, 0, 2, 1] - sm_links: *id058 -- block_parents: [0, 0, 0, 1, 2, 1] - sm_links: *id058 -- block_parents: [0, 0, 1, 1, 2, 1] - sm_links: *id058 -- block_parents: [0, 0, 0, 2, 2, 1] - sm_links: *id058 -- block_parents: [0, 0, 1, 2, 2, 1] - sm_links: *id058 -- block_parents: [0, 0, 0, 0, 3, 1] - sm_links: *id058 -- block_parents: [0, 0, 1, 0, 3, 1] - sm_links: *id058 -- block_parents: [0, 0, 0, 1, 3, 1] - sm_links: *id058 -- block_parents: [0, 0, 1, 1, 3, 1] - sm_links: *id058 -- block_parents: [0, 0, 0, 2, 3, 1] - sm_links: *id058 -- block_parents: [0, 0, 1, 2, 3, 1] - sm_links: *id058 -- block_parents: [0, 0, 0, 0, 0, 2] - sm_links: *id058 -- block_parents: [0, 0, 1, 0, 0, 2] - sm_links: *id058 -- block_parents: [0, 0, 0, 1, 0, 2] - sm_links: *id058 -- block_parents: [0, 0, 1, 1, 0, 2] - sm_links: *id058 -- block_parents: [0, 0, 0, 2, 0, 2] - sm_links: *id058 -- block_parents: [0, 0, 1, 2, 0, 2] - sm_links: *id058 -- block_parents: [0, 0, 0, 0, 1, 2] - sm_links: *id058 -- block_parents: [0, 0, 1, 0, 1, 2] - sm_links: *id058 -- block_parents: [0, 0, 0, 1, 1, 2] - sm_links: *id058 -- block_parents: [0, 0, 1, 1, 1, 2] - sm_links: *id058 -- block_parents: [0, 0, 0, 2, 1, 2] - sm_links: *id058 -- block_parents: [0, 0, 1, 2, 1, 2] - sm_links: *id058 -- block_parents: [0, 0, 0, 0, 2, 2] - sm_links: *id058 -- block_parents: [0, 0, 1, 0, 2, 2] - sm_links: *id058 -- block_parents: [0, 0, 0, 1, 2, 2] - sm_links: *id058 -- block_parents: [0, 0, 1, 1, 2, 2] - sm_links: *id058 -- block_parents: [0, 0, 0, 2, 2, 2] - sm_links: *id058 -- block_parents: [0, 0, 1, 2, 2, 2] - sm_links: *id058 -- block_parents: [0, 0, 0, 0, 3, 2] - sm_links: *id058 -- block_parents: [0, 0, 1, 0, 3, 2] - sm_links: *id058 -- block_parents: [0, 0, 0, 1, 3, 2] - sm_links: *id058 -- block_parents: [0, 0, 1, 1, 3, 2] - sm_links: *id058 -- block_parents: [0, 0, 0, 2, 3, 2] - sm_links: *id058 -- block_parents: [0, 0, 1, 2, 3, 2] - sm_links: *id058 -- block_parents: [0, 0, 0, 0, 0, 3] - sm_links: *id058 -- block_parents: [0, 0, 1, 0, 0, 3] - sm_links: *id058 -- block_parents: [0, 0, 0, 1, 0, 3] - sm_links: *id058 -- block_parents: [0, 0, 1, 1, 0, 3] - sm_links: *id058 -- block_parents: [0, 0, 0, 2, 0, 3] - sm_links: *id058 -- block_parents: [0, 0, 1, 2, 0, 3] - sm_links: *id058 -- block_parents: [0, 0, 0, 0, 1, 3] - sm_links: *id058 -- block_parents: [0, 0, 1, 0, 1, 3] - sm_links: *id058 -- block_parents: [0, 0, 0, 1, 1, 3] - sm_links: *id058 -- block_parents: [0, 0, 1, 1, 1, 3] - sm_links: *id058 -- block_parents: [0, 0, 0, 2, 1, 3] - sm_links: *id058 -- block_parents: [0, 0, 1, 2, 1, 3] - sm_links: *id058 -- block_parents: [0, 0, 0, 0, 2, 3] - sm_links: *id058 -- block_parents: [0, 0, 1, 0, 2, 3] - sm_links: *id058 -- block_parents: [0, 0, 0, 1, 2, 3] - sm_links: *id058 -- block_parents: [0, 0, 1, 1, 2, 3] - sm_links: *id058 -- block_parents: [0, 0, 0, 2, 2, 3] - sm_links: *id058 -- block_parents: [0, 0, 1, 2, 2, 3] - sm_links: *id058 -- block_parents: [0, 0, 0, 0, 3, 3] - sm_links: *id058 -- block_parents: [0, 0, 1, 0, 3, 3] - sm_links: *id058 -- block_parents: [0, 0, 0, 1, 3, 3] - sm_links: *id058 -- block_parents: [0, 0, 1, 1, 3, 3] - sm_links: *id058 -- block_parents: [0, 0, 0, 2, 3, 3] - sm_links: *id058 -- block_parents: [0, 0, 1, 2, 3, 3] - sm_links: *id058 -- block_parents: [0, 0, 0, 0, 0, 4] - sm_links: *id058 -- block_parents: [0, 0, 1, 0, 0, 4] - sm_links: *id058 -- block_parents: [0, 0, 0, 1, 0, 4] - sm_links: *id058 -- block_parents: [0, 0, 1, 1, 0, 4] - sm_links: *id058 -- block_parents: [0, 0, 0, 2, 0, 4] - sm_links: *id058 -- block_parents: [0, 0, 1, 2, 0, 4] - sm_links: *id058 -- block_parents: [0, 0, 0, 0, 1, 4] - sm_links: *id058 -- block_parents: [0, 0, 1, 0, 1, 4] - sm_links: *id058 -- block_parents: [0, 0, 0, 1, 1, 4] - sm_links: *id058 -- block_parents: [0, 0, 1, 1, 1, 4] - sm_links: *id058 -- block_parents: [0, 0, 0, 2, 1, 4] - sm_links: *id058 -- block_parents: [0, 0, 1, 2, 1, 4] - sm_links: *id058 -- block_parents: [0, 0, 0, 0, 2, 4] - sm_links: *id058 -- block_parents: [0, 0, 1, 0, 2, 4] - sm_links: *id058 -- block_parents: [0, 0, 0, 1, 2, 4] - sm_links: *id058 -- block_parents: [0, 0, 1, 1, 2, 4] - sm_links: *id058 -- block_parents: [0, 0, 0, 2, 2, 4] - sm_links: *id058 -- block_parents: [0, 0, 1, 2, 2, 4] - sm_links: *id058 -- block_parents: [0, 0, 0, 0, 3, 4] - sm_links: *id058 -- block_parents: [0, 0, 1, 0, 3, 4] - sm_links: *id058 -- block_parents: [0, 0, 0, 1, 3, 4] - sm_links: *id058 -- block_parents: [0, 0, 1, 1, 3, 4] - sm_links: *id058 -- block_parents: [0, 0, 0, 2, 3, 4] - sm_links: *id058 -- block_parents: [0, 0, 1, 2, 3, 4] - sm_links: *id058 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0] - sm_links: &id059 - - [0, 1] - - [0, 2] - - [2, 3] - - [3, 4] -- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 3, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 3, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 4, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 4, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 4, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 4, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 4, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 4, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 4, 3, 5, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 3, 4, 5, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 4, 4, 5, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 4, 3, 6, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 3, 4, 6, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 4, 4, 6, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 6, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 6, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 6, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 6, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 6, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 6, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 6, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 4, 3, 7, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 7, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 3, 4, 7, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 4, 4, 7, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 7, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 7, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 7, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 7, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 7, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 7, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 7, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 2, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 2, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 2, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 2, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 2, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 2, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 5, 4, 3, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 4, 5, 3, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 5, 5, 3, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 4, 6, 3, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 5, 6, 3, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 2, 4, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 5, 3, 4, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 5, 4, 4, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 5, 4, 4, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 2, 5, 4, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 3, 5, 4, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 4, 5, 4, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 4, 5, 4, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 5, 5, 4, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 5, 5, 4, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 5, 5, 4, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 4, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 2, 6, 4, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 3, 6, 4, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 4, 6, 4, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 4, 6, 4, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 5, 6, 4, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 5, 6, 4, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 5, 6, 4, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 4, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 4, 2, 5, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 2, 5, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 4, 3, 5, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 5, 3, 5, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 2, 4, 5, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 3, 4, 5, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 4, 4, 5, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 4, 4, 5, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 5, 4, 5, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 5, 4, 5, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 5, 4, 5, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 5, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 2, 5, 5, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 3, 5, 5, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 4, 5, 5, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 4, 5, 5, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 4, 5, 5, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 5, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 2, 6, 5, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 3, 6, 5, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 4, 6, 5, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 4, 6, 5, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 4, 6, 5, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 5, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 5, 6, 5, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 5, 6, 5, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 5, 6, 5, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 4, 2, 6, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 2, 6, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 4, 3, 6, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 5, 3, 6, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 2, 4, 6, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 3, 4, 6, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 4, 4, 6, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 4, 4, 6, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 5, 4, 6, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 5, 4, 6, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 5, 4, 6, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 6, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 2, 5, 6, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 3, 5, 6, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 4, 5, 6, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 4, 5, 6, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 4, 5, 6, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 6, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 5, 5, 6, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 5, 5, 6, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 5, 5, 6, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 6, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 2, 6, 6, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 3, 6, 6, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 4, 6, 6, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 4, 6, 6, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 4, 6, 6, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 6, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 5, 6, 6, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 5, 6, 6, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 5, 6, 6, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 6, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 4, 2, 7, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 2, 7, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 4, 3, 7, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 5, 3, 7, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 2, 4, 7, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 3, 4, 7, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 4, 4, 7, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 4, 4, 7, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 5, 4, 7, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 5, 4, 7, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 5, 4, 7, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 4, 7, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 2, 5, 7, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 3, 5, 7, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 4, 5, 7, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 4, 5, 7, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 4, 5, 7, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 4, 5, 7, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 5, 5, 7, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 5, 5, 7, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 5, 5, 7, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 7, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 2, 6, 7, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 3, 6, 7, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 4, 6, 7, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 4, 6, 7, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 4, 6, 7, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 4, 6, 7, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 5, 6, 7, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 5, 6, 7, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 5, 6, 7, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 7, 3, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 2, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 2, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 2, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 2, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 2, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 2, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 2, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 2, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 2, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 2, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 2, 3, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 5, 3, 3, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 5, 4, 3, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 5, 4, 3, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 2, 5, 3, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 3, 5, 3, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 4, 5, 3, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 4, 5, 3, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 5, 5, 3, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 5, 5, 3, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 5, 5, 3, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 5, 3, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 2, 6, 3, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 3, 6, 3, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 4, 6, 3, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 4, 6, 3, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 5, 6, 3, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 5, 6, 3, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 5, 6, 3, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 3, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 5, 2, 4, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 5, 3, 4, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 5, 3, 4, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 2, 5, 4, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 3, 5, 4, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 3, 5, 4, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 5, 5, 4, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 5, 5, 4, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 5, 5, 4, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 2, 6, 4, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 3, 6, 4, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 3, 6, 4, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 5, 6, 4, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 5, 6, 4, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 4, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 3, 2, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 4, 2, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 5, 2, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 2, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 2, 3, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 3, 3, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 4, 3, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 4, 3, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 5, 3, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 5, 3, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 5, 3, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 2, 4, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 3, 4, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 3, 4, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 5, 4, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 5, 4, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 5, 4, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 2, 5, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 2, 5, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 3, 5, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 3, 5, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 3, 5, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 3, 5, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 4, 5, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 4, 5, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 4, 5, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 2, 6, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 2, 6, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 3, 6, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 3, 6, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 3, 6, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 3, 6, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 4, 6, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 4, 6, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 4, 6, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 5, 6, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 5, 6, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 5, 6, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 5, 6, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 6, 5, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 3, 2, 6, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 4, 2, 6, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 5, 2, 6, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 2, 6, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 2, 3, 6, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 3, 3, 6, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 4, 3, 6, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 4, 3, 6, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 5, 3, 6, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 3, 5, 3, 6, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 2, 4, 5, 3, 6, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 4, 5, 3, 6, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 3, 2, 4, 6, 4, 1, 0] - sm_links: *id059 -- block_parents: [0, 0, 1, 2, 3, 2, 3, 4, 6, 4, 1, 0] - sm_links: *id059 diff --git a/tests/generators/fork_choice_generated/smoke/test_gen.yaml b/tests/generators/fork_choice_generated/smoke/test_gen.yaml deleted file mode 100644 index 2deea7b017..0000000000 --- a/tests/generators/fork_choice_generated/smoke/test_gen.yaml +++ /dev/null @@ -1,38 +0,0 @@ -block_tree_test: - test_type: block_tree - instances: smoke/block_tree_tree.yaml # 128 - seed: 123 - nr_variations: 2 - nr_mutations: 1 -block_weight_test: - test_type: block_tree - instances: smoke/block_tree_other.yaml # 4 - seed: 123 - nr_variations: 32 - nr_mutations: 1 -shuffling_test: - test_type: block_tree - instances: smoke/block_tree_other.yaml # 4 - seed: 123 - nr_variations: 2 - nr_mutations: 31 -attester_slashing_test: - test_type: block_tree - instances: smoke/block_tree_other.yaml # 4 - seed: 123 - nr_variations: 8 - nr_mutations: 3 - with_attester_slashings: true -invalid_message_test: - test_type: block_tree - instances: smoke/block_tree_other.yaml # 4 - seed: 123 - nr_variations: 16 - nr_mutations: 1 - with_invalid_messages: true -block_cover_test: - test_type: block_cover - instances: smoke/block_cover.yaml # 24 - seed: 456 - nr_variations: 2 - nr_mutations: 3 From e6505df73ba71085d19fa3a0488d533c720e5839 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 11 Jun 2024 21:20:22 +0400 Subject: [PATCH 082/111] renamed small -> tiny, smoke -> small --- .../fork_choice_generated/small/test_gen.yaml | 12 ++++++------ .../fork_choice_generated/tiny/test_gen.yaml | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/generators/fork_choice_generated/small/test_gen.yaml b/tests/generators/fork_choice_generated/small/test_gen.yaml index 2deea7b017..800d025892 100644 --- a/tests/generators/fork_choice_generated/small/test_gen.yaml +++ b/tests/generators/fork_choice_generated/small/test_gen.yaml @@ -1,38 +1,38 @@ block_tree_test: test_type: block_tree - instances: smoke/block_tree_tree.yaml # 128 + instances: small/block_tree_tree.yaml # 128 seed: 123 nr_variations: 2 nr_mutations: 1 block_weight_test: test_type: block_tree - instances: smoke/block_tree_other.yaml # 4 + instances: small/block_tree_other.yaml # 4 seed: 123 nr_variations: 32 nr_mutations: 1 shuffling_test: test_type: block_tree - instances: smoke/block_tree_other.yaml # 4 + instances: small/block_tree_other.yaml # 4 seed: 123 nr_variations: 2 nr_mutations: 31 attester_slashing_test: test_type: block_tree - instances: smoke/block_tree_other.yaml # 4 + instances: small/block_tree_other.yaml # 4 seed: 123 nr_variations: 8 nr_mutations: 3 with_attester_slashings: true invalid_message_test: test_type: block_tree - instances: smoke/block_tree_other.yaml # 4 + instances: small/block_tree_other.yaml # 4 seed: 123 nr_variations: 16 nr_mutations: 1 with_invalid_messages: true block_cover_test: test_type: block_cover - instances: smoke/block_cover.yaml # 24 + instances: small/block_cover.yaml # 24 seed: 456 nr_variations: 2 nr_mutations: 3 diff --git a/tests/generators/fork_choice_generated/tiny/test_gen.yaml b/tests/generators/fork_choice_generated/tiny/test_gen.yaml index d14ed5cd54..7230ffc893 100644 --- a/tests/generators/fork_choice_generated/tiny/test_gen.yaml +++ b/tests/generators/fork_choice_generated/tiny/test_gen.yaml @@ -1,32 +1,32 @@ block_tree_test: test_type: block_tree - instances: small/block_tree_tree.yaml # 12 + instances: tiny/block_tree_tree.yaml # 12 seed: 123 nr_variations: 2 nr_mutations: 1 block_weight_test: test_type: block_tree - instances: small/block_tree_other.yaml # 3 + instances: tiny/block_tree_other.yaml # 3 seed: 123 nr_variations: 5 nr_mutations: 0 attester_slashing_test: test_type: block_tree - instances: small/block_tree_other.yaml # 3 + instances: tiny/block_tree_other.yaml # 3 seed: 123 nr_variations: 2 nr_mutations: 1 with_attester_slashings: true invalid_message_test: test_type: block_tree - instances: small/block_tree_other.yaml # 3 + instances: tiny/block_tree_other.yaml # 3 seed: 123 nr_variations: 2 nr_mutations: 1 with_invalid_messages: true block_cover_test: test_type: block_cover - instances: small/block_cover.yaml # 12 + instances: tiny/block_cover.yaml # 12 seed: 456 nr_variations: 2 nr_mutations: 1 From 399fd164688066925c31958fb0c0e048d4a2c4ab Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Wed, 12 Jun 2024 05:53:33 +0400 Subject: [PATCH 083/111] Refactoring to improve code structure and ensure deterministic behavior in multiprocessing setup --- .../instance_generator.py | 119 +++------- .../mutation_operators.py | 189 +--------------- .../fork_choice_generated/scheduler.py | 106 +++++++++ .../fork_choice_generated/test_gen.py | 33 ++- .../fork_choice_generated/test_provider.py | 208 ++++++++++++++++++ 5 files changed, 355 insertions(+), 300 deletions(-) create mode 100644 tests/generators/fork_choice_generated/scheduler.py create mode 100644 tests/generators/fork_choice_generated/test_provider.py diff --git a/tests/generators/fork_choice_generated/instance_generator.py b/tests/generators/fork_choice_generated/instance_generator.py index a3f98b3712..7831ccbbed 100644 --- a/tests/generators/fork_choice_generated/instance_generator.py +++ b/tests/generators/fork_choice_generated/instance_generator.py @@ -1,20 +1,16 @@ from eth2spec.test.helpers.constants import ALTAIR from eth2spec.gen_helpers.gen_base import gen_runner from eth2spec.test.helpers.constants import MINIMAL, MAINNET -from eth2spec.test.helpers.specs import spec_targets from eth2spec.gen_helpers.gen_base.gen_typing import TestCase, TestProvider from itertools import product from toolz.dicttoolz import merge from typing import Iterable, Callable -from importlib import import_module from eth2spec.utils import bls from eth2spec.test.helpers.typing import SpecForkName, PresetBaseName from minizinc import Instance, Model, Solver from ruamel.yaml import YAML -from mutation_operators import mk_mutations, MutatorsGenerator import random -from instantiators.block_tree import yield_block_tree_test_case -from instantiators.block_cover import yield_block_cover_test_case +from test_provider import PlainFCTestCase, FCTestDNA BLS_ACTIVE = False @@ -33,7 +29,7 @@ def _create_providers(test_name: str, /, solutions, number_of_variations: int, number_of_mutations: int, - test_fn: Callable, + test_kind: str, ) -> Iterable[TestProvider]: def prepare_fn() -> None: bls.use_milagro() @@ -47,22 +43,22 @@ def prepare_fn() -> None: for fork_name in forks: for preset_name in presets: - spec = spec_targets[preset_name][fork_name] - for i, solution in enumerate(solutions): def make_cases_fn() -> Iterable[TestCase]: for seed in seeds: - mutation_generator = MutatorsGenerator( - fork_name, preset_name, spec, solution, - seed, number_of_mutations, test_fn, debug=debug) for j in range(1 + number_of_mutations): - yield TestCase(fork_name=fork_name, - preset_name=preset_name, - runner_name=GENERATOR_NAME, - handler_name=test_name, - suite_name='pyspec_tests', - case_name=test_name + '_' + str(i) + '_' + str(seed) + '_' + str(j), - case_fn=mutation_generator.next_test_case) + test_dna = FCTestDNA(test_kind, solution, seed, None if j == 0 else seed + j - 1) + yield PlainFCTestCase( + test_dna=test_dna, + bls_active=BLS_ACTIVE, + debug=debug, + fork_name=fork_name, + preset_name=preset_name, + runner_name=GENERATOR_NAME, + handler_name=test_name, + suite_name='pyspec_tests', + case_name=test_name + '_' + str(i) + '_' + str(seed) + '_' + str(j), + ) yield TestProvider(prepare=prepare_fn, make_cases=make_cases_fn) @@ -108,32 +104,6 @@ def _load_block_tree_instances(instance_path: str) -> Iterable[dict]: return solutions -def _create_block_tree_providers(test_name: str, /, - forks: Iterable[SpecForkName], - presets: Iterable[PresetBaseName], - debug: bool, - initial_seed: int, - solutions: Iterable, - number_of_variations: int, - number_of_mutations: int, - with_attester_slashings: bool, - with_invalid_messages: bool) -> Iterable[TestProvider]: - def test_fn(phase: str, preset: str, seed: int, solution): - return yield_block_tree_test_case(generator_mode=True, - phase=phase, - preset=preset, - bls_active=BLS_ACTIVE, - debug=debug, - seed=seed, - sm_links=solution['sm_links'], - block_parents=solution['block_parents'], - with_attester_slashings=with_attester_slashings, - with_invalid_messages=with_invalid_messages) - - yield from _create_providers( - test_name, forks, presets, debug, initial_seed, solutions, number_of_variations, number_of_mutations, test_fn) - - def _find_block_cover_model_solutions(anchor_epoch: int, store_justified_epoch_equal_zero: bool, block_voting_source_epoch_equal_store_justified_epoch: bool, @@ -209,27 +179,6 @@ def _load_block_cover_instances(instance_path: str): return solutions -def _create_block_cover_providers(test_name: str, /, - forks: Iterable[SpecForkName], - presets: Iterable[PresetBaseName], - debug: bool, - initial_seed: int, - solutions, - number_of_variations: int, - number_of_mutations: int) -> Iterable[TestProvider]: - def test_fn(phase: str, preset: str, seed: int, solution): - return yield_block_cover_test_case(generator_mode=True, - phase=phase, - preset=preset, - bls_active=BLS_ACTIVE, - debug=debug, - seed=seed, - model_params=solution) - - yield from _create_providers( - test_name, forks, presets, debug, initial_seed, solutions, number_of_variations, number_of_mutations, test_fn) - - if __name__ == "__main__": arg_parser = gen_runner.create_arg_parser(GENERATOR_NAME) @@ -339,27 +288,19 @@ def test_fn(phase: str, preset: str, seed: int, solution): block_tree_solutions = _find_block_tree_solutions(16, 3, 3) solutions = [merge(*sols) for sols in product(sm_link_solutions, block_tree_solutions)] - if not args.fc_gen_attester_slashings and not args.fc_gen_invalid_messages: + with_attester_slashings, with_invalid_messages = args.fc_gen_attester_slashings, args.fc_gen_invalid_messages + if not with_attester_slashings and not with_invalid_messages: test_name = 'block_tree' - elif args.fc_gen_attester_slashings and not args.fc_gen_invalid_messages: + test_kind = 'block_tree_test' + elif with_attester_slashings and not with_invalid_messages: test_name = 'attester_slashings' - elif args.fc_gen_invalid_messages and not args.fc_gen_attester_slashings: + test_kind = 'attester_slashing_test' + elif with_attester_slashings and with_invalid_messages: test_name = 'invalid_messages' + test_kind = 'invalid_message_test' else: test_name = 'attester_slashings_and_invalid_messages' - - gen_runner.run_generator(GENERATOR_NAME, - _create_block_tree_providers(test_name, - forks=forks, - presets=presets, - debug=args.fc_gen_debug, - initial_seed=args.fc_gen_seed, - solutions=solutions, - number_of_variations=args.fc_gen_variations, - number_of_mutations=args.fc_gen_mutations, - with_attester_slashings=args.fc_gen_attester_slashings, - with_invalid_messages=args.fc_gen_invalid_messages), - arg_parser) + test_kind = 'attestet_slashing_and_invalid_message_test' elif args.fc_gen_test_kind == 'block_cover': if args.fc_gen_instances_path is not None: solutions = _load_block_cover_instances(args.fc_gen_instances_path) @@ -367,16 +308,10 @@ def test_fn(phase: str, preset: str, seed: int, solution): solutions = _generate_block_cover_model_solutions(args.fc_gen_anchor_epoch, args.fc_gen_nr_solutions, args.fc_gen_debug) test_name = 'block_cover' - gen_runner.run_generator(GENERATOR_NAME, - _create_block_cover_providers( - test_name, - forks=forks, - presets=presets, - debug=args.fc_gen_debug, - initial_seed=args.fc_gen_seed, - solutions=solutions, - number_of_variations=args.fc_gen_variations, - number_of_mutations=args.fc_gen_mutations), - arg_parser) + test_kind = 'block_cover_test' else: raise ValueError(f'Unsupported test kind: {args.fc_gen_test_kind}') + + providers = _create_providers(test_name, forks, presets, args.fc_gen_debug, args.fc_gen_seed, + solutions, args.fc_gen_variations, args.fc_gen_mutations, test_kind) + gen_runner.run_generator(GENERATOR_NAME, providers, arg_parser) diff --git a/tests/generators/fork_choice_generated/mutation_operators.py b/tests/generators/fork_choice_generated/mutation_operators.py index 087dae11f2..1f68170aa7 100644 --- a/tests/generators/fork_choice_generated/mutation_operators.py +++ b/tests/generators/fork_choice_generated/mutation_operators.py @@ -1,100 +1,6 @@ -from dataclasses import dataclass -from eth2spec.test.helpers.fork_choice import ( - on_tick_and_append_step, output_store_checks -) -from eth2spec.utils import bls import random -@dataclass -class FCTestCase: - meta: dict - anchor_block: object - anchor_state: object - blocks: dict - atts: dict - slashings: dict - steps: list - - def with_steps(self, steps): - return FCTestCase(self.meta, self.anchor_block, self.anchor_state, self.blocks, self.atts, self.slashings, steps) - - def dump(self): - for k,v in self.meta.items(): - yield k, 'meta', v - yield 'anchor_state', 'ssz', self.anchor_state - yield 'anchor_block', 'ssz', self.anchor_block - for k,v in self.blocks.items(): - yield k, 'ssz', v - for k,v in self.atts.items(): - yield k, 'ssz', v - for k,v in self.slashings.items(): - yield k, 'ssz', v - yield 'steps', 'data', self.steps - - -def parse_test_case(test_case): - meta = {} - anchor_block = None - anchor_state = None - blocks = {} - atts = {} - slashings = {} - steps = None - for i, elem in enumerate(test_case): - assert isinstance(elem, tuple) and len(elem) == 3 - if elem[1] == 'meta': - meta[elem[0]] = elem[2] - elif elem[1] == 'ssz': - if elem[0] == 'anchor_state': - assert anchor_state is None - anchor_state = elem[2] - elif elem[0] == 'anchor_block': - assert anchor_block is None - anchor_block = elem[2] - elif elem[0].startswith('block_'): - blocks[elem[0]] = elem[2] - elif elem[0].startswith('attestation_'): - atts[elem[0]] = elem[2] - elif elem[0].startswith('attester_slashing_'): - slashings[elem[0]] = elem[2] - else: - raise ValueError(f'not implemented {elem[0]}/{elem[1]}') - elif elem[1] == 'data' and elem[0] == 'steps': - assert steps is None - steps = elem[2] - else: - raise ValueError(f'not implemented {elem[0]}/{elem[1]}') - return FCTestCase(meta, anchor_block, anchor_state, blocks, atts, slashings, steps) - - -def steps_to_events(steps): - curr = 0 - events = [] - for step in steps: - if 'tick' in step: - curr = step['tick'] - elif 'block' in step: - events.append((curr, ('block', step['block']))) - elif 'attestation' in step: - events.append((curr, ('attestation', step['attestation']))) - elif 'attester_slashing' in step: - events.append((curr, ('attester_slashing', step['attester_slashing']))) - elif 'checks' in step or 'property_checks' in step: - pass - else: - assert False, step - return events - - -def events_to_steps(events): - steps = [] - for (time, event) in events: - steps.append({'tick': int(time)}) - steps.append({event[0]: event[1]}) - return steps - - def mut_shift_(tv, idx, delta): time, event = tv[idx] new_time = int(time) + delta @@ -126,7 +32,7 @@ def mut_dup_(tv, idx, shift): return mut_shift_(tv + [tv[idx]], len(tv), shift) -def mutate_tc(rnd, initial_tv, cnt, debug=False): +def mutate_test_vector(rnd, initial_tv, cnt, debug=False): tv_ = initial_tv for i in range(cnt): coin = rnd.randint(0, 1) @@ -165,96 +71,3 @@ def mutate_tc(rnd, initial_tv, cnt, debug=False): else: assert False yield tv_ - - -def update_test_case(spec, fc_test_case: FCTestCase, events): - old_bls_state = bls.bls_active - bls.bls_active = False - try: - anchor_state = spec.BeaconState.decode_bytes(fc_test_case.anchor_state) - anchor_block = spec.BeaconBlock.decode_bytes(fc_test_case.anchor_block) - store = spec.get_forkchoice_store(anchor_state, anchor_block) - test_steps = [] - for (time, (kind, event)) in events: - on_tick_and_append_step(spec, store, time, test_steps) - - if kind == 'block': - block_id = event - sb = spec.SignedBeaconBlock.decode_bytes(fc_test_case.blocks[block_id]) - try: - spec.on_block(store, sb) - for attestation in sb.message.body.attestations: - spec.on_attestation(store, attestation, is_from_block=True) - - for attester_slashing in sb.message.body.attester_slashings: - spec.on_attester_slashing(store, attester_slashing) - - valid = True - except AssertionError as e: - valid = False - test_steps.append({'block': block_id, 'valid': valid}) - output_store_checks(spec, store, test_steps) - elif kind == 'attestation': - att_id = event - att = spec.Attestation.decode_bytes(fc_test_case.atts[att_id]) - try: - spec.on_attestation(store, att, is_from_block=False) - valid = True - except AssertionError as e: - valid = False - test_steps.append({'attestation': att_id, 'valid': valid}) - output_store_checks(spec, store, test_steps) - elif kind == 'attester_slashing': - slashing_id = event - slashing = spec.AttesterSlashing.decode_bytes(fc_test_case.slashings[slashing_id]) - try: - spec.on_attester_slashing(store, slashing) - valid = True - except AssertionError as e: - valid = False - test_steps.append({'attester_slashing': slashing_id, 'valid': valid}) - output_store_checks(spec, store, test_steps) - else: - raise ValueError(f'not implemented {kind}') - next_slot_time = store.genesis_time + (spec.get_current_slot(store) + 1) * spec.config.SECONDS_PER_SLOT - on_tick_and_append_step(spec, store, next_slot_time, test_steps) - - return fc_test_case.with_steps(test_steps) - finally: - bls.bls_active = old_bls_state - - -def mk_mutations(spec, seed, num, test_fn, debug=False): - if debug: - print('make base case') - base = list(test_fn()) - yield 0, base - rnd = random.Random(seed) - - fc_test_case = parse_test_case(base) - events = steps_to_events(fc_test_case.steps) - for i, tv_ in enumerate(mutate_tc(rnd, events, num, debug=debug)): - if debug: - print('make mutant', i+1) - yield i+1, update_test_case(spec, fc_test_case, tv_).dump() - - -class MutatorsGenerator: - def __init__(self, fork, preset, spec, solution, seed, num, test_fn, debug=False): - self.fork = fork - self.preset = preset - self.spec = spec - self.solution = solution - self.seed = seed - self.num = num - self.test_fn = test_fn - self.debug = debug - self.iterator = None - - def next_test_case(self): - if self.iterator is None: - def test_inst_fn(): - return self.test_fn(self.fork, self.preset, self.seed, self.solution) - self.iterator = iter(mk_mutations(self.spec, self.seed, self.num, test_inst_fn, self.debug)) - _, test_case = next(self.iterator) - return test_case diff --git a/tests/generators/fork_choice_generated/scheduler.py b/tests/generators/fork_choice_generated/scheduler.py new file mode 100644 index 0000000000..d7062ef548 --- /dev/null +++ b/tests/generators/fork_choice_generated/scheduler.py @@ -0,0 +1,106 @@ +from dataclasses import dataclass, field + + +@dataclass(order=True, init=False) +class QueueItem: + effective_slot: int + is_attestation: bool + message: object = field(compare=False) + dependencies: list = field(compare=False) + is_from_block: bool = field(compare=False) + + def __init__(self, message, is_attestation, is_from_block=False): + self.message = message + self.is_attestation = is_attestation + if is_attestation: + data = message.data + self.effective_slot = data.slot + 1 + self.dependencies = [data.beacon_block_root, data.target.root] + self.is_from_block = is_from_block + else: + block = message.message + self.effective_slot = block.slot + self.dependencies = [block.parent_root] + self.is_from_block = False + + +class MessageScheduler: + def __init__(self, spec, anchor_state, anchor_block): + self.spec = spec + self.store = spec.get_forkchoice_store(anchor_state, anchor_block) + self.message_queue = [] + + def is_early_message(self, item: QueueItem) -> bool: + current_slot = self.spec.get_current_slot(self.store) + return item.effective_slot < current_slot or any(root not in self.store.blocks for root in item.dependencies) + + def enque_message(self, item: QueueItem): + self.message_queue.append(item) + + def drain_queue(self, ) -> list[QueueItem]: + messages = self.message_queue[:] + self.message_queue.clear() + return messages + + def process_queue(self): + updated = False + for item in self.drain_queue(): + if self.is_early_message(item): + self.enque_message(item) + else: + if item.is_attestation: + self.process_attestation(item.message) + else: + updated |= self.process_block(item.message) + return updated + + def purge_queue(self): + while self.process_queue(): + pass + + def process_tick(self, time): + SECONDS_PER_SLOT = self.spec.config.SECONDS_PER_SLOT + assert time >= self.store.time + tick_slot = (time - self.store.genesis_time) // SECONDS_PER_SLOT + while self.spec.get_current_slot(self.store) < tick_slot: + previous_time = self.store.genesis_time + (self.spec.get_current_slot(self.store) + 1) * SECONDS_PER_SLOT + self.spec.on_tick(self.store, previous_time) + self.purge_queue() + + def process_attestation(self, attestation, is_from_block=False): + try: + self.spec.on_attestation(self.store, attestation, is_from_block) + return True + except AssertionError: + item = QueueItem(attestation, True, is_from_block) + if self.is_early_message(item): + self.enque_message(item) + return False + + def process_slashing(self, slashing): + try: + self.spec.on_attester_slashing(self.store, slashing) + return True + except AssertionError: + return False + + def process_block_messages(self, signed_block): + block = signed_block.message + for attestation in block.body.attestations: + self.process_attestation(attestation, is_from_block=True) + for attester_slashing in block.body.attester_slashings: + self.process_slashing(attester_slashing) + + def process_block(self, signed_block): + try: + self.spec.on_block(self.store, signed_block) + valid = True + except AssertionError: + item = QueueItem(signed_block, False) + if self.is_early_message(item): + self.enque_message(item) + valid = False + if valid: + self.purge_queue() + self.process_block_messages(signed_block) + return valid diff --git a/tests/generators/fork_choice_generated/test_gen.py b/tests/generators/fork_choice_generated/test_gen.py index d74eb38cc1..a9f48d0b96 100644 --- a/tests/generators/fork_choice_generated/test_gen.py +++ b/tests/generators/fork_choice_generated/test_gen.py @@ -5,9 +5,8 @@ forks, presets, _load_block_tree_instances, - _create_block_tree_providers, _load_block_cover_instances, - _create_block_cover_providers + _create_providers ) @@ -50,32 +49,26 @@ nr_mutations = params['nr_mutations'] with_attester_slashings = params.get('with_attester_slashings', False) with_invalid_messages = params.get('with_invalid_messages', False) + debug = args.fc_gen_debug if test_type == 'block_tree': solutions = _load_block_tree_instances(instances_path) - providers = _create_block_tree_providers(test_name, - forks=forks, - presets=presets, - debug=args.fc_gen_debug, - initial_seed=initial_seed, - solutions=solutions, - number_of_variations=nr_variations, - number_of_mutations=nr_mutations, - with_attester_slashings=with_attester_slashings, - with_invalid_messages=with_invalid_messages) + if not with_attester_slashings and not with_invalid_messages: + test_kind = 'block_tree_test' + elif with_attester_slashings and not with_invalid_messages: + test_kind = 'attester_slashing_test' + elif not with_attester_slashings and with_invalid_messages: + test_kind = 'invalid_message_test' + else: + test_kind = 'attestet_slashing_and_invalid_message_test' elif test_type == 'block_cover': solutions = _load_block_cover_instances(instances_path) - providers = _create_block_cover_providers(test_name, - forks=forks, - presets=presets, - debug=args.fc_gen_debug, - initial_seed=initial_seed, - solutions=solutions, - number_of_variations=nr_variations, - number_of_mutations=nr_mutations) + test_kind = 'block_cover_test' else: raise ValueError(f'Unsupported test type: {test_type}') + providers = _create_providers(test_name, forks, presets, debug, initial_seed, + solutions, nr_variations, nr_mutations, test_kind) gen_runner.run_generator(GENERATOR_NAME, providers, arg_parser) diff --git a/tests/generators/fork_choice_generated/test_provider.py b/tests/generators/fork_choice_generated/test_provider.py new file mode 100644 index 0000000000..5b9a6622f1 --- /dev/null +++ b/tests/generators/fork_choice_generated/test_provider.py @@ -0,0 +1,208 @@ +from dataclasses import dataclass +from typing import Any, Iterable, Optional, Tuple +from eth2spec.gen_helpers.gen_base.gen_typing import TestCase, TestCasePart +from eth2spec.test.helpers.specs import spec_targets +from eth2spec.test.helpers.fork_choice import ( + on_tick_and_append_step, output_store_checks +) +from eth2spec.utils import bls +from scheduler import MessageScheduler +from instantiators.block_cover import yield_block_cover_test_case +from instantiators.block_tree import yield_block_tree_test_case +from mutation_operators import mutate_test_vector +import random + + +@dataclass +class FCTestDNA: + kind: str + solution: Any + variation_seed: int + mutation_seed: Optional[int] + + +@dataclass(init=False) +class PlainFCTestCase(TestCase): + test_dna: FCTestDNA + bls_active: bool + debug: bool + def __init__(self, test_dna, bls_active=False, debug=False, **kwds): + super().__init__(fork_name=kwds['fork_name'], preset_name=kwds['preset_name'], + runner_name=kwds['runner_name'], handler_name=kwds['handler_name'], + suite_name=kwds['suite_name'], case_name=kwds['case_name'], + case_fn=self.mutation_case_fn) + self.test_dna = test_dna + self.bls_active = bls_active + self.debug = debug + + def mutation_case_fn(self): + spec = spec_targets[self.preset_name][self.fork_name] + base = list(self.plain_case_fn()) + mut_seed = self.test_dna.mutation_seed + if mut_seed is None: + return base + + rnd = random.Random(mut_seed) + fc_test_case = parse_test_case(base) + events = steps_to_events(fc_test_case.steps) + tv_, = list(mutate_test_vector(rnd, events, 1, debug=self.debug)) + mutated_tc = update_test_case(spec, fc_test_case, tv_) + #mutated_tc.meta['mutation_seed'] = mut_seed + return mutated_tc.dump() + + def plain_case_fn(self) -> Iterable[TestCasePart]: + phase, preset = self.fork_name, self.preset_name + bls_active, debug = self.bls_active, self.debug + solution, seed = self.test_dna.solution, self.test_dna.variation_seed + if self.test_dna.kind in ['block_tree_test', 'attester_slashing_test', 'invalid_message_test']: + with_attester_slashings = self.test_dna.kind == 'attester_slashing_test' + with_invalid_messages = self.test_dna.kind == 'invalid_message_test' + return yield_block_tree_test_case( + generator_mode=True, + phase=phase, preset=preset, + bls_active=bls_active, debug=debug, + seed=seed, sm_links=solution['sm_links'], block_parents=solution['block_parents'], + with_attester_slashings=with_attester_slashings, with_invalid_messages=with_invalid_messages) + elif self.test_dna.kind == 'block_cover_test': + return yield_block_cover_test_case( + generator_mode=True, + phase=phase, preset=preset, + bls_active=bls_active, debug=debug, + seed=seed, model_params=solution) + else: + raise ValueError(f'Unknown FC test kind {self.test_dna.kind}') + + +@dataclass +class FCTestCase: + meta: dict + anchor_block: object + anchor_state: object + blocks: dict + atts: dict + slashings: dict + steps: list + + def with_steps(self, steps): + return FCTestCase(self.meta, self.anchor_block, self.anchor_state, self.blocks, self.atts, self.slashings, steps) + + def dump(self): + for k,v in self.meta.items(): + yield k, 'meta', v + yield 'anchor_state', 'ssz', self.anchor_state + yield 'anchor_block', 'ssz', self.anchor_block + for k,v in self.blocks.items(): + yield k, 'ssz', v + for k,v in self.atts.items(): + yield k, 'ssz', v + for k,v in self.slashings.items(): + yield k, 'ssz', v + yield 'steps', 'data', self.steps + + +def parse_test_case(test_case): + meta = {} + anchor_block = None + anchor_state = None + blocks = {} + atts = {} + slashings = {} + steps = None + for i, elem in enumerate(test_case): + assert isinstance(elem, tuple) and len(elem) == 3 + if elem[1] == 'meta': + meta[elem[0]] = elem[2] + elif elem[1] == 'ssz': + if elem[0] == 'anchor_state': + assert anchor_state is None + anchor_state = elem[2] + elif elem[0] == 'anchor_block': + assert anchor_block is None + anchor_block = elem[2] + elif elem[0].startswith('block_'): + blocks[elem[0]] = elem[2] + elif elem[0].startswith('attestation_'): + atts[elem[0]] = elem[2] + elif elem[0].startswith('attester_slashing_'): + slashings[elem[0]] = elem[2] + else: + raise ValueError(f'not implemented {elem[0]}/{elem[1]}') + elif elem[1] == 'data' and elem[0] == 'steps': + assert steps is None + steps = elem[2] + else: + raise ValueError(f'not implemented {elem[0]}/{elem[1]}') + return FCTestCase(meta, anchor_block, anchor_state, blocks, atts, slashings, steps) + + +def update_test_case(spec, fc_test_case: FCTestCase, events): + old_bls_state = bls.bls_active + bls.bls_active = False + try: + anchor_state = spec.BeaconState.decode_bytes(fc_test_case.anchor_state) + anchor_block = spec.BeaconBlock.decode_bytes(fc_test_case.anchor_block) + store = spec.get_forkchoice_store(anchor_state, anchor_block) + test_steps = [] + scheduler = MessageScheduler(spec, anchor_state, anchor_block) + + for (time, (kind, event)) in events: + scheduler.process_tick(time) + on_tick_and_append_step(spec, store, time, test_steps) + + # output checks after applying buffered messages, since they affect store state + output_store_checks(spec, store, test_steps) + + if kind == 'block': + block_id = event + sb = spec.SignedBeaconBlock.decode_bytes(fc_test_case.blocks[block_id]) + valid = scheduler.process_block(sb) + test_steps.append({'block': block_id, 'valid': valid}) + output_store_checks(spec, store, test_steps) + elif kind == 'attestation': + att_id = event + att = spec.Attestation.decode_bytes(fc_test_case.atts[att_id]) + valid = scheduler.process_attestation(att, is_from_block=False) + test_steps.append({'attestation': att_id, 'valid': valid}) + output_store_checks(spec, store, test_steps) + elif kind == 'attester_slashing': + slashing_id = event + slashing = spec.AttesterSlashing.decode_bytes(fc_test_case.slashings[slashing_id]) + valid = scheduler.process_slashing(slashing) + test_steps.append({'attester_slashing': slashing_id, 'valid': valid}) + output_store_checks(spec, store, test_steps) + else: + raise ValueError(f'not implemented {kind}') + next_slot_time = store.genesis_time + (spec.get_current_slot(store) + 1) * spec.config.SECONDS_PER_SLOT + # on_tick_and_append_step(spec, store, next_slot_time, test_steps, checks_with_viable_for_head_weights=True) + on_tick_and_append_step(spec, store, next_slot_time, test_steps) + + return fc_test_case.with_steps(test_steps) + finally: + bls.bls_active = old_bls_state + + +def steps_to_events(steps): + curr = 0 + events = [] + for step in steps: + if 'tick' in step: + curr = step['tick'] + elif 'block' in step: + events.append((curr, ('block', step['block']))) + elif 'attestation' in step: + events.append((curr, ('attestation', step['attestation']))) + elif 'attester_slashing' in step: + events.append((curr, ('attester_slashing', step['attester_slashing']))) + elif 'checks' in step or 'property_checks' in step: + pass + else: + assert False, step + return events + + +def events_to_steps(events): + steps = [] + for (time, event) in events: + steps.append({'tick': int(time)}) + steps.append({event[0]: event[1]}) + return steps From bb56274fa56e12bb9877f080407a8690e4e8c809 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Wed, 12 Jun 2024 06:05:03 +0400 Subject: [PATCH 084/111] Code restructuring --- .../instance_generator.py | 54 +------------------ .../fork_choice_generated/test_gen.py | 7 +-- .../fork_choice_generated/test_provider.py | 48 ++++++++++++++++- 3 files changed, 51 insertions(+), 58 deletions(-) diff --git a/tests/generators/fork_choice_generated/instance_generator.py b/tests/generators/fork_choice_generated/instance_generator.py index 7831ccbbed..45387a06be 100644 --- a/tests/generators/fork_choice_generated/instance_generator.py +++ b/tests/generators/fork_choice_generated/instance_generator.py @@ -1,68 +1,18 @@ from eth2spec.test.helpers.constants import ALTAIR from eth2spec.gen_helpers.gen_base import gen_runner from eth2spec.test.helpers.constants import MINIMAL, MAINNET -from eth2spec.gen_helpers.gen_base.gen_typing import TestCase, TestProvider from itertools import product from toolz.dicttoolz import merge from typing import Iterable, Callable -from eth2spec.utils import bls -from eth2spec.test.helpers.typing import SpecForkName, PresetBaseName from minizinc import Instance, Model, Solver from ruamel.yaml import YAML -import random -from test_provider import PlainFCTestCase, FCTestDNA - - -BLS_ACTIVE = False -GENERATOR_NAME = 'fork_choice_generated' +from test_provider import GENERATOR_NAME, create_providers forks = [ALTAIR] presets = [MINIMAL] -def _create_providers(test_name: str, /, - forks: Iterable[SpecForkName], - presets: Iterable[PresetBaseName], - debug: bool, - initial_seed: int, - solutions, - number_of_variations: int, - number_of_mutations: int, - test_kind: str, - ) -> Iterable[TestProvider]: - def prepare_fn() -> None: - bls.use_milagro() - return - - seeds = [initial_seed] - if number_of_variations > 1: - rnd = random.Random(initial_seed) - seeds = [rnd.randint(1, 10000) for _ in range(number_of_variations)] - seeds[0] = initial_seed - - for fork_name in forks: - for preset_name in presets: - for i, solution in enumerate(solutions): - def make_cases_fn() -> Iterable[TestCase]: - for seed in seeds: - for j in range(1 + number_of_mutations): - test_dna = FCTestDNA(test_kind, solution, seed, None if j == 0 else seed + j - 1) - yield PlainFCTestCase( - test_dna=test_dna, - bls_active=BLS_ACTIVE, - debug=debug, - fork_name=fork_name, - preset_name=preset_name, - runner_name=GENERATOR_NAME, - handler_name=test_name, - suite_name='pyspec_tests', - case_name=test_name + '_' + str(i) + '_' + str(seed) + '_' + str(j), - ) - - yield TestProvider(prepare=prepare_fn, make_cases=make_cases_fn) - - def _find_sm_link_solutions(anchor_epoch: int, number_of_epochs: int, number_of_links: int) -> Iterable[Iterable[tuple]]: @@ -312,6 +262,6 @@ def _load_block_cover_instances(instance_path: str): else: raise ValueError(f'Unsupported test kind: {args.fc_gen_test_kind}') - providers = _create_providers(test_name, forks, presets, args.fc_gen_debug, args.fc_gen_seed, + providers = create_providers(test_name, forks, presets, args.fc_gen_debug, args.fc_gen_seed, solutions, args.fc_gen_variations, args.fc_gen_mutations, test_kind) gen_runner.run_generator(GENERATOR_NAME, providers, arg_parser) diff --git a/tests/generators/fork_choice_generated/test_gen.py b/tests/generators/fork_choice_generated/test_gen.py index a9f48d0b96..18b8416d53 100644 --- a/tests/generators/fork_choice_generated/test_gen.py +++ b/tests/generators/fork_choice_generated/test_gen.py @@ -6,16 +6,13 @@ presets, _load_block_tree_instances, _load_block_cover_instances, - _create_providers ) +from test_provider import GENERATOR_NAME, create_providers yaml = YAML(typ='safe') -GENERATOR_NAME = 'fork_choice_generated' - - if __name__ == "__main__": arg_parser = gen_runner.create_arg_parser(GENERATOR_NAME) @@ -67,7 +64,7 @@ else: raise ValueError(f'Unsupported test type: {test_type}') - providers = _create_providers(test_name, forks, presets, debug, initial_seed, + providers = create_providers(test_name, forks, presets, debug, initial_seed, solutions, nr_variations, nr_mutations, test_kind) gen_runner.run_generator(GENERATOR_NAME, providers, arg_parser) diff --git a/tests/generators/fork_choice_generated/test_provider.py b/tests/generators/fork_choice_generated/test_provider.py index 5b9a6622f1..3446b146c5 100644 --- a/tests/generators/fork_choice_generated/test_provider.py +++ b/tests/generators/fork_choice_generated/test_provider.py @@ -1,10 +1,11 @@ from dataclasses import dataclass from typing import Any, Iterable, Optional, Tuple -from eth2spec.gen_helpers.gen_base.gen_typing import TestCase, TestCasePart +from eth2spec.gen_helpers.gen_base.gen_typing import TestCase, TestCasePart, TestProvider from eth2spec.test.helpers.specs import spec_targets from eth2spec.test.helpers.fork_choice import ( on_tick_and_append_step, output_store_checks ) +from eth2spec.test.helpers.typing import SpecForkName, PresetBaseName from eth2spec.utils import bls from scheduler import MessageScheduler from instantiators.block_cover import yield_block_cover_test_case @@ -13,6 +14,10 @@ import random +BLS_ACTIVE = False +GENERATOR_NAME = 'fork_choice_generated' + + @dataclass class FCTestDNA: kind: str @@ -206,3 +211,44 @@ def events_to_steps(events): steps.append({'tick': int(time)}) steps.append({event[0]: event[1]}) return steps + +def create_providers(test_name: str, /, + forks: Iterable[SpecForkName], + presets: Iterable[PresetBaseName], + debug: bool, + initial_seed: int, + solutions, + number_of_variations: int, + number_of_mutations: int, + test_kind: str, + ) -> Iterable[TestProvider]: + def prepare_fn() -> None: + bls.use_milagro() + return + + seeds = [initial_seed] + if number_of_variations > 1: + rnd = random.Random(initial_seed) + seeds = [rnd.randint(1, 10000) for _ in range(number_of_variations)] + seeds[0] = initial_seed + + for fork_name in forks: + for preset_name in presets: + for i, solution in enumerate(solutions): + def make_cases_fn() -> Iterable[TestCase]: + for seed in seeds: + for j in range(1 + number_of_mutations): + test_dna = FCTestDNA(test_kind, solution, seed, None if j == 0 else seed + j - 1) + yield PlainFCTestCase( + test_dna=test_dna, + bls_active=BLS_ACTIVE, + debug=debug, + fork_name=fork_name, + preset_name=preset_name, + runner_name=GENERATOR_NAME, + handler_name=test_name, + suite_name='pyspec_tests', + case_name=test_name + '_' + str(i) + '_' + str(seed) + '_' + str(j), + ) + + yield TestProvider(prepare=prepare_fn, make_cases=make_cases_fn) From c9c345b14b536c3bd4db453873d8e9dcb551318d Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 18 Jun 2024 23:57:53 +0400 Subject: [PATCH 085/111] refactored code, to use test data from instantiators, instead of full test case info --- .../instantiators/block_cover.py | 8 + .../instantiators/block_tree.py | 20 +++ .../fork_choice_generated/test_provider.py | 162 +++++++++--------- 3 files changed, 109 insertions(+), 81 deletions(-) diff --git a/tests/generators/fork_choice_generated/instantiators/block_cover.py b/tests/generators/fork_choice_generated/instantiators/block_cover.py index 4ed60521ce..4546ceb9a8 100644 --- a/tests/generators/fork_choice_generated/instantiators/block_cover.py +++ b/tests/generators/fork_choice_generated/instantiators/block_cover.py @@ -276,3 +276,11 @@ def yield_block_cover_test_case(spec, state, model_params=None, debug=False, see yield from yield_fork_choice_test_case(spec, store, test_data, debug) # Run sanity checks against model params run_sanity_checks(spec, store, model_params, target_block_root) + +@with_altair_and_later +@spec_state_test +def yield_block_cover_test_data(spec, state, model_params=None, debug=False, seed=1): + test_data, _ = gen_block_cover_test_data(spec, state, model_params, debug, seed) + yield 'test_data', test_data + # Run sanity checks against model params + # run_sanity_checks(spec, store, model_params, target_block_root) diff --git a/tests/generators/fork_choice_generated/instantiators/block_tree.py b/tests/generators/fork_choice_generated/instantiators/block_tree.py index b627b7ffc9..ff82ad66c0 100644 --- a/tests/generators/fork_choice_generated/instantiators/block_tree.py +++ b/tests/generators/fork_choice_generated/instantiators/block_tree.py @@ -593,3 +593,23 @@ def yield_block_tree_test_case(spec, store = spec.get_forkchoice_store(test_data.anchor_state, test_data.anchor_block) yield from yield_fork_choice_test_case(spec, store, test_data, debug) + +@with_altair_and_later +@spec_state_test +def yield_block_tree_test_data(spec, + state, + debug=False, + seed=1, + sm_links=None, + block_parents=None, + with_attester_slashings=False, + with_invalid_messages=False): + # This test is mainly used for the test generation purposes + # Thus seed, sm_links and block_parents are provided by the generator + # Define sm_links, seed and block_parents explicitly to execute a certain run of this test + if sm_links is None or block_parents is None: + return + + test_data = gen_block_tree_test_data(spec, state, debug, seed, sm_links, block_parents, + with_attester_slashings, with_invalid_messages) + yield 'test_data', test_data diff --git a/tests/generators/fork_choice_generated/test_provider.py b/tests/generators/fork_choice_generated/test_provider.py index 3446b146c5..d16ebe3dd1 100644 --- a/tests/generators/fork_choice_generated/test_provider.py +++ b/tests/generators/fork_choice_generated/test_provider.py @@ -3,13 +3,17 @@ from eth2spec.gen_helpers.gen_base.gen_typing import TestCase, TestCasePart, TestProvider from eth2spec.test.helpers.specs import spec_targets from eth2spec.test.helpers.fork_choice import ( - on_tick_and_append_step, output_store_checks + on_tick_and_append_step, output_store_checks, + get_block_file_name, + get_attestation_file_name, + get_attester_slashing_file_name, ) from eth2spec.test.helpers.typing import SpecForkName, PresetBaseName from eth2spec.utils import bls from scheduler import MessageScheduler -from instantiators.block_cover import yield_block_cover_test_case -from instantiators.block_tree import yield_block_tree_test_case +from instantiators.block_cover import yield_block_cover_test_case, yield_block_cover_test_data +from instantiators.block_tree import yield_block_tree_test_case, yield_block_tree_test_data +from instantiators.helpers import FCTestData from mutation_operators import mutate_test_vector import random @@ -42,34 +46,38 @@ def __init__(self, test_dna, bls_active=False, debug=False, **kwds): def mutation_case_fn(self): spec = spec_targets[self.preset_name][self.fork_name] - base = list(self.plain_case_fn()) + mut_seed = self.test_dna.mutation_seed if mut_seed is None: - return base - - rnd = random.Random(mut_seed) - fc_test_case = parse_test_case(base) - events = steps_to_events(fc_test_case.steps) - tv_, = list(mutate_test_vector(rnd, events, 1, debug=self.debug)) - mutated_tc = update_test_case(spec, fc_test_case, tv_) - #mutated_tc.meta['mutation_seed'] = mut_seed - return mutated_tc.dump() + return list(self.call_instantiator(test_data_only=False)) + else: + test_data = list(self.call_instantiator(test_data_only=True)) + fc_test_case, events = parse_test_data(spec, test_data) + tv_, = list(mutate_test_vector(random.Random(mut_seed), events, 1, debug=self.debug)) + mutated_tc = update_test_case(spec, fc_test_case, tv_) + #mutated_tc.meta['mutation_seed'] = mut_seed + return mutated_tc.dump() def plain_case_fn(self) -> Iterable[TestCasePart]: + yield from self.call_instantiator(test_data_only=False) + + def call_instantiator(self, test_data_only) -> Iterable[TestCasePart]: phase, preset = self.fork_name, self.preset_name bls_active, debug = self.bls_active, self.debug solution, seed = self.test_dna.solution, self.test_dna.variation_seed if self.test_dna.kind in ['block_tree_test', 'attester_slashing_test', 'invalid_message_test']: with_attester_slashings = self.test_dna.kind == 'attester_slashing_test' with_invalid_messages = self.test_dna.kind == 'invalid_message_test' - return yield_block_tree_test_case( + instantiator_fn = yield_block_tree_test_data if test_data_only else yield_block_tree_test_case + return instantiator_fn( generator_mode=True, phase=phase, preset=preset, bls_active=bls_active, debug=debug, seed=seed, sm_links=solution['sm_links'], block_parents=solution['block_parents'], with_attester_slashings=with_attester_slashings, with_invalid_messages=with_invalid_messages) elif self.test_dna.kind == 'block_cover_test': - return yield_block_cover_test_case( + instantiator_fn = yield_block_cover_test_data if test_data_only else yield_block_cover_test_case + return instantiator_fn( generator_mode=True, phase=phase, preset=preset, bls_active=bls_active, debug=debug, @@ -86,7 +94,7 @@ class FCTestCase: blocks: dict atts: dict slashings: dict - steps: list + steps: Optional[list] def with_steps(self, steps): return FCTestCase(self.meta, self.anchor_block, self.anchor_state, self.blocks, self.atts, self.slashings, steps) @@ -94,58 +102,76 @@ def with_steps(self, steps): def dump(self): for k,v in self.meta.items(): yield k, 'meta', v - yield 'anchor_state', 'ssz', self.anchor_state - yield 'anchor_block', 'ssz', self.anchor_block + yield 'anchor_state', 'ssz', self.anchor_state.encode_bytes() + yield 'anchor_block', 'ssz', self.anchor_block.encode_bytes() for k,v in self.blocks.items(): - yield k, 'ssz', v + yield k, 'ssz', v.encode_bytes() for k,v in self.atts.items(): - yield k, 'ssz', v + yield k, 'ssz', v.encode_bytes() for k,v in self.slashings.items(): - yield k, 'ssz', v + yield k, 'ssz', v.encode_bytes() yield 'steps', 'data', self.steps -def parse_test_case(test_case): - meta = {} - anchor_block = None - anchor_state = None +def parse_test_data(spec, _test_data) -> Tuple[FCTestCase, list[Any]]: + (elem_0, elem_1, elem_2), = _test_data + assert elem_1 == 'data' and elem_0 == 'test_data' + + test_data: FCTestData = elem_2 + meta = test_data.meta + anchor_state = test_data.anchor_state + anchor_block = test_data.anchor_block blocks = {} atts = {} slashings = {} - steps = None - for i, elem in enumerate(test_case): - assert isinstance(elem, tuple) and len(elem) == 3 - if elem[1] == 'meta': - meta[elem[0]] = elem[2] - elif elem[1] == 'ssz': - if elem[0] == 'anchor_state': - assert anchor_state is None - anchor_state = elem[2] - elif elem[0] == 'anchor_block': - assert anchor_block is None - anchor_block = elem[2] - elif elem[0].startswith('block_'): - blocks[elem[0]] = elem[2] - elif elem[0].startswith('attestation_'): - atts[elem[0]] = elem[2] - elif elem[0].startswith('attester_slashing_'): - slashings[elem[0]] = elem[2] - else: - raise ValueError(f'not implemented {elem[0]}/{elem[1]}') - elif elem[1] == 'data' and elem[0] == 'steps': - assert steps is None - steps = elem[2] + events = [] + + store = spec.get_forkchoice_store(test_data.anchor_state, test_data.anchor_block) + + def get_time(slot): + return int(slot * spec.config.SECONDS_PER_SLOT + store.genesis_time) + + for b in test_data.blocks: + signed_block = b.payload + event_id = get_block_file_name(signed_block) + blocks[event_id] = signed_block + events.append((get_time(signed_block.message.slot), ('block', event_id))) + + for a in test_data.atts: + attestation = a.payload + event_id = get_attestation_file_name(attestation) + atts[event_id] = attestation + events.append((get_time(attestation.data.slot + 1), ('attestation', event_id))) + + for s in test_data.slashings: + slashing = s.payload + event_id = get_attester_slashing_file_name(slashing) + effective_slot = max(slashing.attestation_1.data.slot, slashing.attestation_2.data.slot) + 1 + slashings[event_id] = slashing + events.append((get_time(effective_slot), ('attester_slashing', event_id))) + + def get_key(event): + time, (event_type, _) = event + if event_type == 'block': + prio = 2 + elif event_type == 'attestation': + prio = 0 else: - raise ValueError(f'not implemented {elem[0]}/{elem[1]}') - return FCTestCase(meta, anchor_block, anchor_state, blocks, atts, slashings, steps) + assert event_type == 'attester_slashing' + prio = 1 + return (time, prio) + + events = sorted(events, key=get_key) + + return FCTestCase(meta, anchor_block, anchor_state, blocks, atts, slashings, []), events def update_test_case(spec, fc_test_case: FCTestCase, events): old_bls_state = bls.bls_active bls.bls_active = False try: - anchor_state = spec.BeaconState.decode_bytes(fc_test_case.anchor_state) - anchor_block = spec.BeaconBlock.decode_bytes(fc_test_case.anchor_block) + anchor_state = fc_test_case.anchor_state + anchor_block = fc_test_case.anchor_block store = spec.get_forkchoice_store(anchor_state, anchor_block) test_steps = [] scheduler = MessageScheduler(spec, anchor_state, anchor_block) @@ -159,19 +185,19 @@ def update_test_case(spec, fc_test_case: FCTestCase, events): if kind == 'block': block_id = event - sb = spec.SignedBeaconBlock.decode_bytes(fc_test_case.blocks[block_id]) + sb = fc_test_case.blocks[block_id] valid = scheduler.process_block(sb) test_steps.append({'block': block_id, 'valid': valid}) output_store_checks(spec, store, test_steps) elif kind == 'attestation': att_id = event - att = spec.Attestation.decode_bytes(fc_test_case.atts[att_id]) + att = fc_test_case.atts[att_id] valid = scheduler.process_attestation(att, is_from_block=False) test_steps.append({'attestation': att_id, 'valid': valid}) output_store_checks(spec, store, test_steps) elif kind == 'attester_slashing': slashing_id = event - slashing = spec.AttesterSlashing.decode_bytes(fc_test_case.slashings[slashing_id]) + slashing = fc_test_case.slashings[slashing_id] valid = scheduler.process_slashing(slashing) test_steps.append({'attester_slashing': slashing_id, 'valid': valid}) output_store_checks(spec, store, test_steps) @@ -186,32 +212,6 @@ def update_test_case(spec, fc_test_case: FCTestCase, events): bls.bls_active = old_bls_state -def steps_to_events(steps): - curr = 0 - events = [] - for step in steps: - if 'tick' in step: - curr = step['tick'] - elif 'block' in step: - events.append((curr, ('block', step['block']))) - elif 'attestation' in step: - events.append((curr, ('attestation', step['attestation']))) - elif 'attester_slashing' in step: - events.append((curr, ('attester_slashing', step['attester_slashing']))) - elif 'checks' in step or 'property_checks' in step: - pass - else: - assert False, step - return events - - -def events_to_steps(events): - steps = [] - for (time, event) in events: - steps.append({'tick': int(time)}) - steps.append({event[0]: event[1]}) - return steps - def create_providers(test_name: str, /, forks: Iterable[SpecForkName], presets: Iterable[PresetBaseName], From 638642b34f4686f105f9dd9f981e2355b6910f5d Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Wed, 19 Jun 2024 07:24:38 +0400 Subject: [PATCH 086/111] Code reorg: make helper methods --- .../fork_choice_generated/test_gen.py | 78 +++++++++++-------- 1 file changed, 45 insertions(+), 33 deletions(-) diff --git a/tests/generators/fork_choice_generated/test_gen.py b/tests/generators/fork_choice_generated/test_gen.py index 18b8416d53..70b363c6dc 100644 --- a/tests/generators/fork_choice_generated/test_gen.py +++ b/tests/generators/fork_choice_generated/test_gen.py @@ -10,10 +10,49 @@ from test_provider import GENERATOR_NAME, create_providers -yaml = YAML(typ='safe') +def run_test_group(test_name, test_type, instances_path, + initial_seed, nr_variations, nr_mutations, + with_attester_slashings, with_invalid_messages, + debug=False, arg_parser=None): + if test_type == 'block_tree': + solutions = _load_block_tree_instances(instances_path) + if not with_attester_slashings and not with_invalid_messages: + test_kind = 'block_tree_test' + elif with_attester_slashings and not with_invalid_messages: + test_kind = 'attester_slashing_test' + elif not with_attester_slashings and with_invalid_messages: + test_kind = 'invalid_message_test' + else: + test_kind = 'attestet_slashing_and_invalid_message_test' + elif test_type == 'block_cover': + solutions = _load_block_cover_instances(instances_path) + test_kind = 'block_cover_test' + else: + raise ValueError(f'Unsupported test type: {test_type}') + + providers = create_providers(test_name, forks, presets, debug, initial_seed, + solutions, nr_variations, nr_mutations, test_kind) + gen_runner.run_generator(GENERATOR_NAME, providers, arg_parser) -if __name__ == "__main__": +def run_test_config(test_gen_config, debug=False, arg_parser=None): + for test_name, params in test_gen_config.items(): + print(test_name) + test_type = params['test_type'] + instances_path = params['instances'] + initial_seed = params['seed'] + nr_variations = params['nr_variations'] + nr_mutations = params['nr_mutations'] + with_attester_slashings = params.get('with_attester_slashings', False) + with_invalid_messages = params.get('with_invalid_messages', False) + + run_test_group(test_name, test_type, instances_path, + initial_seed, nr_variations, nr_mutations, + with_attester_slashings, with_invalid_messages, + debug=debug, arg_parser=arg_parser) + + +def main(): arg_parser = gen_runner.create_arg_parser(GENERATOR_NAME) arg_parser.add_argument( @@ -35,37 +74,10 @@ args = arg_parser.parse_args() with open(args.fc_gen_config, 'r') as f: + yaml = YAML(typ='safe') test_gen_config = yaml.load(f) - for test_name, params in test_gen_config.items(): - print(test_name) - test_type = params['test_type'] - instances_path = params['instances'] - initial_seed = params['seed'] - nr_variations = params['nr_variations'] - nr_mutations = params['nr_mutations'] - with_attester_slashings = params.get('with_attester_slashings', False) - with_invalid_messages = params.get('with_invalid_messages', False) - debug = args.fc_gen_debug - - if test_type == 'block_tree': - solutions = _load_block_tree_instances(instances_path) - if not with_attester_slashings and not with_invalid_messages: - test_kind = 'block_tree_test' - elif with_attester_slashings and not with_invalid_messages: - test_kind = 'attester_slashing_test' - elif not with_attester_slashings and with_invalid_messages: - test_kind = 'invalid_message_test' - else: - test_kind = 'attestet_slashing_and_invalid_message_test' - elif test_type == 'block_cover': - solutions = _load_block_cover_instances(instances_path) - test_kind = 'block_cover_test' - else: - raise ValueError(f'Unsupported test type: {test_type}') - - providers = create_providers(test_name, forks, presets, debug, initial_seed, - solutions, nr_variations, nr_mutations, test_kind) - gen_runner.run_generator(GENERATOR_NAME, providers, arg_parser) - + run_test_config(test_gen_config, debug = args.fc_gen_debug, arg_parser=arg_parser) +if __name__ == "__main__": + main() From fd21a460831322eabea2e5ae1f24200bd4433e22 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Wed, 19 Jun 2024 07:25:16 +0400 Subject: [PATCH 087/111] split `yield_fork_choice_test_case` into two steps: - make steps/events (tick|block|att|slashing) from messages - execute the steps/events --- .../instantiators/helpers.py | 114 +++++++++++++----- 1 file changed, 84 insertions(+), 30 deletions(-) diff --git a/tests/generators/fork_choice_generated/instantiators/helpers.py b/tests/generators/fork_choice_generated/instantiators/helpers.py index c3250ec599..32c80037b1 100644 --- a/tests/generators/fork_choice_generated/instantiators/helpers.py +++ b/tests/generators/fork_choice_generated/instantiators/helpers.py @@ -16,6 +16,9 @@ add_attester_slashing, add_block, output_store_checks, + run_on_attestation, + run_on_attester_slashing, + run_on_block ) from .debug_helpers import print_head @@ -233,23 +236,26 @@ def advance_state_to_anchor_epoch(spec, state, anchor_epoch, debug) -> ([], Bran return signed_blocks, anchor_tip -def _on_tick_and_append_step(spec, store, slot, test_steps): - time = slot * spec.config.SECONDS_PER_SLOT + store.genesis_time - on_tick_and_append_step(spec, store, time, test_steps) - assert store.time == time - - -def yield_fork_choice_test_case(spec, store, test_data: FCTestData, debug: bool): - # Yield meta - for k, v in test_data.meta.items(): - yield k, 'meta', v - - # Yield anchor state and block initialization - yield 'anchor_state', test_data.anchor_state - yield 'anchor_block', test_data.anchor_block - - test_steps = [] - _on_tick_and_append_step(spec, store, test_data.anchor_state.slot, test_steps) +def make_events(spec, genesis_time, initial_store_time, test_data: FCTestData) -> list[tuple[int, object, bool]]: + """ + Makes test events from `test_data`'s blocks, attestations and slashings, sorted by an effective slot. + Each event is a triple ('tick'|'block'|'attestation'|'attester_slashing', message, valid). + """ + store_time = initial_store_time + test_events = [] + + def slot_to_time(slot): + return slot * spec.config.SECONDS_PER_SLOT + genesis_time + + def add_tick_step(time): + test_events.append(('tick', time, None)) + nonlocal store_time + store_time = time + + def add_message_step(kind, message): + test_events.append((kind, message.payload, message.valid)) + + add_tick_step(slot_to_time(test_data.anchor_state.slot)) # Apply generated messages max_block_slot = max(b.payload.message.slot for b in test_data.blocks) @@ -262,37 +268,80 @@ def yield_fork_choice_test_case(spec, store, test_data: FCTestData, debug: bool) end_slot = max(max_block_slot, max_attestation_slot, max_slashing_slot) # Advance time to start_slot - _on_tick_and_append_step(spec, store, start_slot, test_steps) + add_tick_step(slot_to_time(start_slot)) # Apply messages to store for slot in range(start_slot, end_slot + 1): # on_tick - _on_tick_and_append_step(spec, store, slot, test_steps) + add_tick_step(slot_to_time(slot)) # on_attestation for attestations from the previous slot for attestation_message in (a for a in test_data.atts if a.payload.data.slot == slot - 1): - yield from add_attestation(spec, store, attestation_message.payload, - test_steps, valid=attestation_message.valid) + add_message_step('attestation', attestation_message) # on_attester_slashing for slashing from the previous slot for attester_slashing_message in (s for s in test_data.slashings if max(s.payload.attestation_1.data.slot, s.payload.attestation_2.data.slot) == slot - 1): - yield from add_attester_slashing(spec, store, attester_slashing_message.payload, - test_steps, valid=attester_slashing_message.valid) + add_message_step('attester_slashing', attester_slashing_message) # on_block for blocks from the current slot for signed_block_message in (b for b in test_data.blocks if b.payload.message.slot == slot): - yield from add_block(spec, store, signed_block_message.payload, test_steps, signed_block_message.valid) + add_message_step('block', signed_block_message) + + if store_time < test_data.store_final_time: + add_tick_step(test_data.store_final_time) + + return test_events + - block_root = signed_block_message.payload.message.hash_tree_root() - if signed_block_message.valid: - assert store.blocks[block_root] == signed_block_message.payload.message +def yield_fork_choice_test_events(spec, store, test_data: FCTestData, test_events: list, debug: bool): + # Yield meta + for k, v in test_data.meta.items(): + yield k, 'meta', v + + # Yield anchor state and block initialization + yield 'anchor_state', test_data.anchor_state + yield 'anchor_block', test_data.anchor_block + + test_steps = [] + + def try_add_mesage(runner, message): + try: + runner(spec, store, message, valid=True) + return True + except AssertionError: + return False + + for event in test_events: + event_kind = event[0] + if event_kind == 'tick': + _, time, _ = event + on_tick_and_append_step(spec, store, time, test_steps) + assert store.time == time + elif event_kind == 'block': + _, signed_block, valid = event + if valid is None: + valid = try_add_mesage(run_on_block, signed_block) + yield from add_block(spec, store, signed_block, test_steps, valid=valid) + + block_root = signed_block.message.hash_tree_root() + if valid: + assert store.blocks[block_root] == signed_block.message else: assert block_root not in store.blocks.values() - - if store.time < test_data.store_final_time: - on_tick_and_append_step(spec, store, test_data.store_final_time, test_steps) + elif event_kind == 'attestation': + _, attestation, valid = event + if valid is None: + valid = try_add_mesage(run_on_attestation, attestation) + yield from add_attestation(spec, store, attestation, test_steps, valid=valid) + elif event_kind == 'attester_slashing': + _, attester_slashing, valid = event + if valid is None: + valid = try_add_mesage(run_on_attester_slashing, attester_slashing) + yield from add_attester_slashing(spec, store, attester_slashing, test_steps, valid=valid) + else: + raise ValueError('Unknown event ' + str(event_kind)) if debug: print(' head: ' + print_head(spec, store)) @@ -300,3 +349,8 @@ def yield_fork_choice_test_case(spec, store, test_data: FCTestData, debug: bool) output_store_checks(spec, store, test_steps, with_viable_for_head_weights=True) yield 'steps', test_steps + + +def yield_fork_choice_test_case(spec, store, test_data: FCTestData, debug: bool): + test_events = make_events(spec, store.genesis_time, store.time, test_data) + yield from yield_fork_choice_test_events(spec, store, test_data, test_events, debug) From aa91e8d1eb393cef3e00c0f2240c0d48ee476708 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Thu, 20 Jun 2024 03:52:43 +0400 Subject: [PATCH 088/111] refactoring of mutated test generation --- .../instantiators/helpers.py | 73 ++++---- .../fork_choice_generated/test_provider.py | 158 +++++++----------- 2 files changed, 98 insertions(+), 133 deletions(-) diff --git a/tests/generators/fork_choice_generated/instantiators/helpers.py b/tests/generators/fork_choice_generated/instantiators/helpers.py index 32c80037b1..a88c913dc5 100644 --- a/tests/generators/fork_choice_generated/instantiators/helpers.py +++ b/tests/generators/fork_choice_generated/instantiators/helpers.py @@ -236,12 +236,12 @@ def advance_state_to_anchor_epoch(spec, state, anchor_epoch, debug) -> ([], Bran return signed_blocks, anchor_tip -def make_events(spec, genesis_time, initial_store_time, test_data: FCTestData) -> list[tuple[int, object, bool]]: +def make_events(spec, test_data: FCTestData) -> list[tuple[int, object, bool]]: """ Makes test events from `test_data`'s blocks, attestations and slashings, sorted by an effective slot. Each event is a triple ('tick'|'block'|'attestation'|'attester_slashing', message, valid). """ - store_time = initial_store_time + genesis_time = test_data.anchor_state.genesis_time test_events = [] def slot_to_time(slot): @@ -249,47 +249,44 @@ def slot_to_time(slot): def add_tick_step(time): test_events.append(('tick', time, None)) - nonlocal store_time - store_time = time def add_message_step(kind, message): test_events.append((kind, message.payload, message.valid)) add_tick_step(slot_to_time(test_data.anchor_state.slot)) - # Apply generated messages - max_block_slot = max(b.payload.message.slot for b in test_data.blocks) - max_attestation_slot = max(a.payload.data.slot for a in test_data.atts) + 1 if any(test_data.atts) else 0 - max_slashing_slot = max(max(s.payload.attestation_1.data.slot, - s.payload.attestation_2.data.slot) - for s in test_data.slashings) + 1 if any(test_data.slashings) else 0 - - start_slot = min(b.payload.message.slot for b in test_data.blocks) - end_slot = max(max_block_slot, max_attestation_slot, max_slashing_slot) - - # Advance time to start_slot - add_tick_step(slot_to_time(start_slot)) - - # Apply messages to store - for slot in range(start_slot, end_slot + 1): - # on_tick - add_tick_step(slot_to_time(slot)) - - # on_attestation for attestations from the previous slot - for attestation_message in (a for a in test_data.atts if a.payload.data.slot == slot - 1): - add_message_step('attestation', attestation_message) - - # on_attester_slashing for slashing from the previous slot - for attester_slashing_message in (s for s in test_data.slashings - if max(s.payload.attestation_1.data.slot, - s.payload.attestation_2.data.slot) == slot - 1): - add_message_step('attester_slashing', attester_slashing_message) - - # on_block for blocks from the current slot - for signed_block_message in (b for b in test_data.blocks if b.payload.message.slot == slot): - add_message_step('block', signed_block_message) - - if store_time < test_data.store_final_time: + def get_seffective_slot(message): + event_kind, data, _ = message + if event_kind == 'block': + return data.message.slot + elif event_kind == 'attestation': + return data.data.slot + 1 + elif event_kind == 'attester_slashing': + return max(data.attestation_1.data.slot, data.attestation_1.data.slot) + 1 + else: + assert False + + messages = [('attestation', m.payload, m.valid) for m in test_data.atts] \ + + [('attester_slashing', m.payload, m.valid) for m in test_data.slashings] \ + + [('block', m.payload, m.valid) for m in test_data.blocks] + + slot = None + + for event in sorted(messages, key=get_seffective_slot): + event_kind, message, valid = event + event_slot = get_seffective_slot(event) + if slot is None: + slot = event_slot + # record tick twice for compatibility with prior code + add_tick_step(slot_to_time(slot)) + add_tick_step(slot_to_time(slot)) + else: + while slot < event_slot: + slot += 1 + add_tick_step(slot_to_time(slot)) + add_message_step(event_kind, ProtocolMessage(message, valid)) + + if slot is None or slot_to_time(slot) < test_data.store_final_time: add_tick_step(test_data.store_final_time) return test_events @@ -352,5 +349,5 @@ def try_add_mesage(runner, message): def yield_fork_choice_test_case(spec, store, test_data: FCTestData, debug: bool): - test_events = make_events(spec, store.genesis_time, store.time, test_data) + test_events = make_events(spec, test_data) yield from yield_fork_choice_test_events(spec, store, test_data, test_events, debug) diff --git a/tests/generators/fork_choice_generated/test_provider.py b/tests/generators/fork_choice_generated/test_provider.py index d16ebe3dd1..46f8f583ef 100644 --- a/tests/generators/fork_choice_generated/test_provider.py +++ b/tests/generators/fork_choice_generated/test_provider.py @@ -13,7 +13,7 @@ from scheduler import MessageScheduler from instantiators.block_cover import yield_block_cover_test_case, yield_block_cover_test_data from instantiators.block_tree import yield_block_tree_test_case, yield_block_tree_test_data -from instantiators.helpers import FCTestData +from instantiators.helpers import FCTestData, make_events, yield_fork_choice_test_events from mutation_operators import mutate_test_vector import random @@ -51,12 +51,14 @@ def mutation_case_fn(self): if mut_seed is None: return list(self.call_instantiator(test_data_only=False)) else: - test_data = list(self.call_instantiator(test_data_only=True)) - fc_test_case, events = parse_test_data(spec, test_data) - tv_, = list(mutate_test_vector(random.Random(mut_seed), events, 1, debug=self.debug)) - mutated_tc = update_test_case(spec, fc_test_case, tv_) + test_data = list(self.call_instantiator(test_data_only=True))[0][2] + test_vector = events_to_test_vector(make_events(spec, test_data)) + mutated_vector, = list(mutate_test_vector(random.Random(mut_seed), test_vector, 1, debug=self.debug)) #mutated_tc.meta['mutation_seed'] = mut_seed - return mutated_tc.dump() + + mutated_events = test_vector_to_events(mutated_vector) + + return yield_test_parts(spec, test_data, mutated_vector) def plain_case_fn(self) -> Iterable[TestCasePart]: yield from self.call_instantiator(test_data_only=False) @@ -86,92 +88,61 @@ def call_instantiator(self, test_data_only) -> Iterable[TestCasePart]: raise ValueError(f'Unknown FC test kind {self.test_dna.kind}') -@dataclass -class FCTestCase: - meta: dict - anchor_block: object - anchor_state: object - blocks: dict - atts: dict - slashings: dict - steps: Optional[list] - - def with_steps(self, steps): - return FCTestCase(self.meta, self.anchor_block, self.anchor_state, self.blocks, self.atts, self.slashings, steps) - - def dump(self): - for k,v in self.meta.items(): - yield k, 'meta', v - yield 'anchor_state', 'ssz', self.anchor_state.encode_bytes() - yield 'anchor_block', 'ssz', self.anchor_block.encode_bytes() - for k,v in self.blocks.items(): - yield k, 'ssz', v.encode_bytes() - for k,v in self.atts.items(): - yield k, 'ssz', v.encode_bytes() - for k,v in self.slashings.items(): - yield k, 'ssz', v.encode_bytes() - yield 'steps', 'data', self.steps - - -def parse_test_data(spec, _test_data) -> Tuple[FCTestCase, list[Any]]: - (elem_0, elem_1, elem_2), = _test_data - assert elem_1 == 'data' and elem_0 == 'test_data' - - test_data: FCTestData = elem_2 - meta = test_data.meta - anchor_state = test_data.anchor_state - anchor_block = test_data.anchor_block - blocks = {} - atts = {} - slashings = {} - events = [] - - store = spec.get_forkchoice_store(test_data.anchor_state, test_data.anchor_block) - - def get_time(slot): - return int(slot * spec.config.SECONDS_PER_SLOT + store.genesis_time) - - for b in test_data.blocks: - signed_block = b.payload - event_id = get_block_file_name(signed_block) - blocks[event_id] = signed_block - events.append((get_time(signed_block.message.slot), ('block', event_id))) - - for a in test_data.atts: - attestation = a.payload - event_id = get_attestation_file_name(attestation) - atts[event_id] = attestation - events.append((get_time(attestation.data.slot + 1), ('attestation', event_id))) - - for s in test_data.slashings: - slashing = s.payload - event_id = get_attester_slashing_file_name(slashing) - effective_slot = max(slashing.attestation_1.data.slot, slashing.attestation_2.data.slot) + 1 - slashings[event_id] = slashing - events.append((get_time(effective_slot), ('attester_slashing', event_id))) - - def get_key(event): - time, (event_type, _) = event - if event_type == 'block': - prio = 2 - elif event_type == 'attestation': - prio = 0 +def events_to_test_vector(events) -> list[Any]: + test_vector = [] + current_time = None + for event in events: + event_kind, data, _ = event + if event_kind == 'tick': + current_time = data else: - assert event_type == 'attester_slashing' - prio = 1 - return (time, prio) - - events = sorted(events, key=get_key) + if event_kind == 'block': + event_id = data + elif event_kind == 'attestation': + event_id = data + elif event_kind == 'attester_slashing': + event_id = data + else: + assert False, event_kind + test_vector.append((current_time, (event_kind, event_id))) + return test_vector + - return FCTestCase(meta, anchor_block, anchor_state, blocks, atts, slashings, []), events +def test_vector_to_events(test_vector): + events = [] + current_time = None + for time, (event_kind, data) in test_vector: + if time != current_time: + current_time = time + events.append(('tick', time, None)) + events.append((event_kind, data, None)) + return events -def update_test_case(spec, fc_test_case: FCTestCase, events): +def yield_test_parts(spec, test_data: FCTestData, events): old_bls_state = bls.bls_active bls.bls_active = False try: - anchor_state = fc_test_case.anchor_state - anchor_block = fc_test_case.anchor_block + for k,v in test_data.meta.items(): + yield k, 'meta', v + + yield 'anchor_state', 'ssz', test_data.anchor_state.encode_bytes() + yield 'anchor_block', 'ssz', test_data.anchor_block.encode_bytes() + + for message in test_data.blocks: + block = message.payload + yield get_block_file_name(block), 'ssz', block.encode_bytes() + + for message in test_data.atts: + attestation = message.payload + yield get_attestation_file_name(attestation), 'ssz', attestation.encode_bytes() + + for message in test_data.slashings: + attester_slashing = message.payload + yield get_attester_slashing_file_name(attester_slashing), 'ssz', attester_slashing.encode_bytes() + + anchor_state = test_data.anchor_state + anchor_block = test_data.anchor_block store = spec.get_forkchoice_store(anchor_state, anchor_block) test_steps = [] scheduler = MessageScheduler(spec, anchor_state, anchor_block) @@ -184,21 +155,18 @@ def update_test_case(spec, fc_test_case: FCTestCase, events): output_store_checks(spec, store, test_steps) if kind == 'block': - block_id = event - sb = fc_test_case.blocks[block_id] - valid = scheduler.process_block(sb) + block_id = get_block_file_name(event) + valid = scheduler.process_block(event) test_steps.append({'block': block_id, 'valid': valid}) output_store_checks(spec, store, test_steps) elif kind == 'attestation': - att_id = event - att = fc_test_case.atts[att_id] - valid = scheduler.process_attestation(att, is_from_block=False) + att_id = get_attestation_file_name(event) + valid = scheduler.process_attestation(event, is_from_block=False) test_steps.append({'attestation': att_id, 'valid': valid}) output_store_checks(spec, store, test_steps) elif kind == 'attester_slashing': - slashing_id = event - slashing = fc_test_case.slashings[slashing_id] - valid = scheduler.process_slashing(slashing) + slashing_id = get_attester_slashing_file_name(event) + valid = scheduler.process_slashing(event) test_steps.append({'attester_slashing': slashing_id, 'valid': valid}) output_store_checks(spec, store, test_steps) else: @@ -207,7 +175,7 @@ def update_test_case(spec, fc_test_case: FCTestCase, events): # on_tick_and_append_step(spec, store, next_slot_time, test_steps, checks_with_viable_for_head_weights=True) on_tick_and_append_step(spec, store, next_slot_time, test_steps) - return fc_test_case.with_steps(test_steps) + yield 'steps', 'data', test_steps finally: bls.bls_active = old_bls_state From 7fe26e60ba0f6562ca7c4586f8f926fd82e10a15 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Thu, 20 Jun 2024 06:24:55 +0400 Subject: [PATCH 089/111] re-worked test generation: skip duplicate tick events --- .../instantiators/helpers.py | 38 ++++++---- .../fork_choice_generated/test_provider.py | 71 ++++++++++--------- 2 files changed, 65 insertions(+), 44 deletions(-) diff --git a/tests/generators/fork_choice_generated/instantiators/helpers.py b/tests/generators/fork_choice_generated/instantiators/helpers.py index a88c913dc5..c41ecf5677 100644 --- a/tests/generators/fork_choice_generated/instantiators/helpers.py +++ b/tests/generators/fork_choice_generated/instantiators/helpers.py @@ -1,5 +1,6 @@ from dataclasses import dataclass, field from .debug_helpers import print_epoch +from eth2spec.utils.ssz.ssz_typing import View from eth2spec.test.helpers.state import ( next_slot, ) @@ -254,6 +255,7 @@ def add_message_step(kind, message): test_events.append((kind, message.payload, message.valid)) add_tick_step(slot_to_time(test_data.anchor_state.slot)) + slot = test_data.anchor_state.slot def get_seffective_slot(message): event_kind, data, _ = message @@ -270,20 +272,12 @@ def get_seffective_slot(message): + [('attester_slashing', m.payload, m.valid) for m in test_data.slashings] \ + [('block', m.payload, m.valid) for m in test_data.blocks] - slot = None - for event in sorted(messages, key=get_seffective_slot): event_kind, message, valid = event event_slot = get_seffective_slot(event) - if slot is None: - slot = event_slot - # record tick twice for compatibility with prior code - add_tick_step(slot_to_time(slot)) + while slot < event_slot: + slot += 1 add_tick_step(slot_to_time(slot)) - else: - while slot < event_slot: - slot += 1 - add_tick_step(slot_to_time(slot)) add_message_step(event_kind, ProtocolMessage(message, valid)) if slot is None or slot_to_time(slot) < test_data.store_final_time: @@ -292,6 +286,25 @@ def get_seffective_slot(message): return test_events +def filter_out_duplicate_messages(fn): + def wrapper(*args, **kwargs): + processed_keys = set() + for data in fn(*args, **kwargs): + if len(data) != 2: + yield data + else: + (key, value) = data + if value is not None and isinstance(value, (bytes, View)): + # skip already processed ssz parts + if key not in processed_keys: + processed_keys.add(key) + yield data + else: + yield data + return wrapper + + +@filter_out_duplicate_messages def yield_fork_choice_test_events(spec, store, test_data: FCTestData, test_events: list, debug: bool): # Yield meta for k, v in test_data.meta.items(): @@ -314,8 +327,9 @@ def try_add_mesage(runner, message): event_kind = event[0] if event_kind == 'tick': _, time, _ = event - on_tick_and_append_step(spec, store, time, test_steps) - assert store.time == time + if time > store.time: + on_tick_and_append_step(spec, store, time, test_steps) + assert store.time == time elif event_kind == 'block': _, signed_block, valid = event if valid is None: diff --git a/tests/generators/fork_choice_generated/test_provider.py b/tests/generators/fork_choice_generated/test_provider.py index 46f8f583ef..8c6fdc26d0 100644 --- a/tests/generators/fork_choice_generated/test_provider.py +++ b/tests/generators/fork_choice_generated/test_provider.py @@ -1,6 +1,7 @@ from dataclasses import dataclass from typing import Any, Iterable, Optional, Tuple from eth2spec.gen_helpers.gen_base.gen_typing import TestCase, TestCasePart, TestProvider +from eth2spec.test.context import spec_test from eth2spec.test.helpers.specs import spec_targets from eth2spec.test.helpers.fork_choice import ( on_tick_and_append_step, output_store_checks, @@ -13,7 +14,7 @@ from scheduler import MessageScheduler from instantiators.block_cover import yield_block_cover_test_case, yield_block_cover_test_data from instantiators.block_tree import yield_block_tree_test_case, yield_block_tree_test_data -from instantiators.helpers import FCTestData, make_events, yield_fork_choice_test_events +from instantiators.helpers import FCTestData, make_events, yield_fork_choice_test_events, filter_out_duplicate_messages from mutation_operators import mutate_test_vector import random @@ -48,17 +49,23 @@ def mutation_case_fn(self): spec = spec_targets[self.preset_name][self.fork_name] mut_seed = self.test_dna.mutation_seed + test_data = list(self.call_instantiator(test_data_only=True))[0][2] + events = make_events(spec, test_data) + store = spec.get_forkchoice_store(test_data.anchor_state, test_data.anchor_block) if mut_seed is None: - return list(self.call_instantiator(test_data_only=False)) + return (spec_test(yield_fork_choice_test_events))( + spec, store, test_data, events, self.debug, generator_mode=True, bls_active=self.bls_active) else: - test_data = list(self.call_instantiator(test_data_only=True))[0][2] - test_vector = events_to_test_vector(make_events(spec, test_data)) + test_vector = events_to_test_vector(events) mutated_vector, = list(mutate_test_vector(random.Random(mut_seed), test_vector, 1, debug=self.debug)) #mutated_tc.meta['mutation_seed'] = mut_seed mutated_events = test_vector_to_events(mutated_vector) - return yield_test_parts(spec, test_data, mutated_vector) + # return (spec_test(yield_fork_choice_test_events))( + # spec, store, test_data, mutated_events, self.debug, generator_mode=True, bls_active=self.bls_active) + return (spec_test(yield_test_parts))( + spec, store, test_data, mutated_events, generator_mode=True, bls_active=self.bls_active) def plain_case_fn(self) -> Iterable[TestCasePart]: yield from self.call_instantiator(test_data_only=False) @@ -119,54 +126,56 @@ def test_vector_to_events(test_vector): return events -def yield_test_parts(spec, test_data: FCTestData, events): - old_bls_state = bls.bls_active - bls.bls_active = False - try: +@filter_out_duplicate_messages +def yield_test_parts(spec, store, test_data: FCTestData, events): for k,v in test_data.meta.items(): yield k, 'meta', v - yield 'anchor_state', 'ssz', test_data.anchor_state.encode_bytes() - yield 'anchor_block', 'ssz', test_data.anchor_block.encode_bytes() + yield 'anchor_state', test_data.anchor_state + yield 'anchor_block', test_data.anchor_block for message in test_data.blocks: block = message.payload - yield get_block_file_name(block), 'ssz', block.encode_bytes() + yield get_block_file_name(block), block.encode_bytes() for message in test_data.atts: attestation = message.payload - yield get_attestation_file_name(attestation), 'ssz', attestation.encode_bytes() + yield get_attestation_file_name(attestation), attestation.encode_bytes() for message in test_data.slashings: attester_slashing = message.payload - yield get_attester_slashing_file_name(attester_slashing), 'ssz', attester_slashing.encode_bytes() + yield get_attester_slashing_file_name(attester_slashing), attester_slashing.encode_bytes() anchor_state = test_data.anchor_state anchor_block = test_data.anchor_block - store = spec.get_forkchoice_store(anchor_state, anchor_block) test_steps = [] scheduler = MessageScheduler(spec, anchor_state, anchor_block) - for (time, (kind, event)) in events: - scheduler.process_tick(time) - on_tick_and_append_step(spec, store, time, test_steps) - - # output checks after applying buffered messages, since they affect store state - output_store_checks(spec, store, test_steps) - - if kind == 'block': - block_id = get_block_file_name(event) - valid = scheduler.process_block(event) + for (kind, data, _) in events: + if kind == 'tick': + time = data + if time > store.time: + scheduler.process_tick(time) + on_tick_and_append_step(spec, store, time, test_steps) + + # output checks after applying buffered messages, since they affect store state + output_store_checks(spec, store, test_steps) + elif kind == 'block': + block = data + block_id = get_block_file_name(block) + valid = scheduler.process_block(block) test_steps.append({'block': block_id, 'valid': valid}) output_store_checks(spec, store, test_steps) elif kind == 'attestation': - att_id = get_attestation_file_name(event) - valid = scheduler.process_attestation(event, is_from_block=False) + attestation = data + att_id = get_attestation_file_name(attestation) + valid = scheduler.process_attestation(attestation, is_from_block=False) test_steps.append({'attestation': att_id, 'valid': valid}) output_store_checks(spec, store, test_steps) elif kind == 'attester_slashing': - slashing_id = get_attester_slashing_file_name(event) - valid = scheduler.process_slashing(event) + attester_slashing = data + slashing_id = get_attester_slashing_file_name(attester_slashing) + valid = scheduler.process_slashing(attester_slashing) test_steps.append({'attester_slashing': slashing_id, 'valid': valid}) output_store_checks(spec, store, test_steps) else: @@ -175,9 +184,7 @@ def yield_test_parts(spec, test_data: FCTestData, events): # on_tick_and_append_step(spec, store, next_slot_time, test_steps, checks_with_viable_for_head_weights=True) on_tick_and_append_step(spec, store, next_slot_time, test_steps) - yield 'steps', 'data', test_steps - finally: - bls.bls_active = old_bls_state + yield 'steps', test_steps def create_providers(test_name: str, /, From 8233c8ca73cdf791d9245c4da39f10764eb51c3b Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 25 Jun 2024 09:54:22 +0400 Subject: [PATCH 090/111] Links added: - previous repo - testing methodoolgy description --- tests/generators/fork_choice_generated/README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/generators/fork_choice_generated/README.md b/tests/generators/fork_choice_generated/README.md index e67b115ba1..c27645d5cc 100644 --- a/tests/generators/fork_choice_generated/README.md +++ b/tests/generators/fork_choice_generated/README.md @@ -1,5 +1,9 @@ -# Fork choice tests +# Fork choice compliance test generator -Fork choice tests cover the different forking cases with fork choice helper functions. +Fork Choice test generator intended to produce tests to validate conformance to the specs of various Fork Choice implementations. -Information on the format of the tests can be found in the [fork choice test formats documentation](../../formats/fork_choice/README.md). +Implementation of the approach described in the [Fork Choice compliance testing framework](https://hackmd.io/@ericsson49/fork-choice-implementation-vs-spec-testing). + +Preliminary research has been also performed in this [repo](https://github.com/txrx-research/fork_choice_test_generation/tree/main). + +To simplfy adoption of the tests, we follow the test format described in the [fork choice test formats documentation](../../formats/fork_choice/README.md), with a minor exception (new check added). From 05bbbc70846ed47bc7a7f6c0de6e8efc25cb2ef0 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Fri, 28 Jun 2024 01:48:30 +0400 Subject: [PATCH 091/111] Test runner added --- .../fork_choice_generated/test_run.py | 172 ++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 tests/generators/fork_choice_generated/test_run.py diff --git a/tests/generators/fork_choice_generated/test_run.py b/tests/generators/fork_choice_generated/test_run.py new file mode 100644 index 0000000000..7ca9a39851 --- /dev/null +++ b/tests/generators/fork_choice_generated/test_run.py @@ -0,0 +1,172 @@ +import argparse +from collections import namedtuple +from glob import glob +from pathlib import Path +from pathos.multiprocessing import ProcessingPool as Pool +from ruamel.yaml import YAML +from snappy import uncompress +from tqdm import tqdm +from typing import Iterable + + +from eth2spec.gen_helpers.gen_base import settings +from eth2spec.test.helpers.specs import spec_targets +from eth2spec.utils import bls + + +bls.bls_active = False + + +def read_yaml(fp): + with open(fp) as f: + yaml = YAML(typ='safe') + return yaml.load(f.read()) + +def read_ssz_snappy(fp): + with open(fp, 'rb') as f: + res = uncompress(f.read()) + return res + + +def get_test_case(spec, td): + def get_prefix(p): + return p[p.rindex('/')+1:p.rindex('.')] + return (read_yaml(f'{td}/meta.yaml'), + spec.BeaconBlock.decode_bytes(read_ssz_snappy(f'{td}/anchor_block.ssz_snappy')), + spec.BeaconState.decode_bytes(read_ssz_snappy(f'{td}/anchor_state.ssz_snappy')), + {get_prefix(b): spec.SignedBeaconBlock.decode_bytes(read_ssz_snappy(b)) for b in glob(f'{td}/block_*.ssz_snappy')}, + {get_prefix(b): spec.Attestation.decode_bytes(read_ssz_snappy(b)) for b in glob(f'{td}/attestation_*.ssz_snappy')}, + {get_prefix(b): spec.AttesterSlashing.decode_bytes(read_ssz_snappy(b)) for b in glob(f'{td}/attester_slashing_*.ssz_snappy')}, + read_yaml(f'{td}/steps.yaml')) + + +TestInfo = namedtuple('TestInfo', ['preset', 'fork', 'test_dir',]) + + +def run_test(test_info): + preset, fork, test_dir = test_info + spec = spec_targets[preset][fork] + meta, anchor_block, anchor_state, blocks, atts, slashings, steps = get_test_case(spec, test_dir) + store = spec.get_forkchoice_store(anchor_state, anchor_block) + for step in steps: + if 'tick' in step: + time = step['tick'] + spec.on_tick(store, time) + elif 'block' in step: + block_id = step['block'] + valid = step.get('valid', True) + recovery = step.get('recovery', False) + signed_block = blocks[block_id] + if valid: + spec.on_block(store, signed_block) + for block_att in signed_block.message.body.attestations: + try: + spec.on_attestation(store, block_att, is_from_block=True) + except AssertionError: + pass + for block_att_slashing in signed_block.message.body.attester_slashings: + try: + spec.on_attester_slashing(store, block_att_slashing) + except AssertionError: + pass + else: + try: + spec.on_block(store, signed_block) + assert False + except AssertionError: + pass + elif 'attestation' in step: + att_id = step['attestation'] + valid = step.get('valid', True) + recovery = step.get('recovery', False) + attestation = atts[att_id] + if valid: + spec.on_attestation(store, attestation, is_from_block=False) + else: + try: + spec.on_attestation(store, attestation, is_from_block=False) + assert False + except AssertionError: + pass + elif 'attester_slashing' in step: + slashing_id = step['attester_slashing'] + valid = step.get('valid', True) + recovery = step.get('recovery', False) + assert valid + slashing = slashings[slashing_id] + spec.on_attester_slashing(store, slashing) + elif 'checks' in step: + checks = step['checks'] + for check, value in checks.items(): + if check == 'time': + expected_time = value + assert store.time == expected_time + elif check == 'head': + assert str(spec.get_head(store)) == value['root'] + elif check == 'proposer_boost_root': + assert str(store.proposer_boost_root) == str(value) + elif check == 'justified_checkpoint': + checkpoint = store.justified_checkpoint + assert checkpoint.epoch == value['epoch'] + assert str(checkpoint.root) == str(value['root']) + elif check == 'finalized_checkpoint': + checkpoint = store.finalized_checkpoint + assert checkpoint.epoch == value['epoch'] + assert str(checkpoint.root) == str(value['root']) + elif check == 'viable_for_head_roots_and_weights': + filtered_block_roots = spec.get_filtered_block_tree(store).keys() + leaves_viable_for_head = [root for root in filtered_block_roots + if not any(c for c in filtered_block_roots if store.blocks[c].parent_root == root)] + viable_for_head_roots_and_weights = { + str(viable_for_head_root): int(spec.get_weight(store, viable_for_head_root)) + for viable_for_head_root in leaves_viable_for_head + } + assert value == viable_for_head_roots_and_weights + else: + assert False + else: + assert False + + +def gather_tests(tests_dir) -> Iterable[TestInfo]: + for preset in [p.name for p in Path(tests_dir).glob('*') if p.name in spec_targets]: + for fork in [f.name for f in (Path(tests_dir) / preset).glob('*') if f.name in spec_targets[preset]]: + print(f'{preset}/{fork}') + for test_dir in sorted([td for td in (Path(tests_dir) / preset / fork).glob('*/*/*/*')]): + yield TestInfo(preset, fork, test_dir) + + +def runt_tests_parallel(tests_dir, num_proc=settings.NUM_PROCESS): + def runner(test_info: TestInfo): + try: + run_test(test_info) + except Exception as e: + raise e + + tests = list(gather_tests(tests_dir)) + with Pool(processes=num_proc) as pool: + for _ in tqdm(pool.imap(runner, tests), total=len(tests)): + pass + + +def run_tests(tests_dir): + for test_info in gather_tests(tests_dir): + print(test_info.test_dir) + run_test(test_info) + + +def main(): + arg_parser = argparse.ArgumentParser() + arg_parser.add_argument( + "-i", + "--test-dir", + dest="test_dir", + required=True, + help="directory with generated tests" + ) + args = arg_parser.parse_args() + runt_tests_parallel(args.test_dir) + + +if __name__ == '__main__': + main() \ No newline at end of file From 3cec3f1bea9c52c45f981274c771e4c1017c854a Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Fri, 28 Jun 2024 01:51:58 +0400 Subject: [PATCH 092/111] Typos fixed - increase amount of tests for shuffling test --- tests/generators/fork_choice_generated/standard/test_gen.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/generators/fork_choice_generated/standard/test_gen.yaml b/tests/generators/fork_choice_generated/standard/test_gen.yaml index 3137c28986..d052871525 100644 --- a/tests/generators/fork_choice_generated/standard/test_gen.yaml +++ b/tests/generators/fork_choice_generated/standard/test_gen.yaml @@ -14,8 +14,8 @@ shuffling_test: test_type: block_tree instances: standard/block_tree_other.yaml # 8 seed: 6673 - nr_variations: 1 - nr_mutations: 20 + nr_variations: 4 + nr_mutations: 63 attester_slashing_test: test_type: block_tree instances: standard/block_tree_other.yaml # 8 From c947d0c6039f1536e79faff7fe534ae5a7e8ea9a Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Fri, 28 Jun 2024 01:56:33 +0400 Subject: [PATCH 093/111] Fixed some bugs with scheduling --- .../instantiators/helpers.py | 71 ++++++++++++-- .../fork_choice_generated/scheduler.py | 45 ++++++--- .../fork_choice_generated/test_provider.py | 96 +++++++++++++++---- 3 files changed, 171 insertions(+), 41 deletions(-) diff --git a/tests/generators/fork_choice_generated/instantiators/helpers.py b/tests/generators/fork_choice_generated/instantiators/helpers.py index c41ecf5677..1ccb056160 100644 --- a/tests/generators/fork_choice_generated/instantiators/helpers.py +++ b/tests/generators/fork_choice_generated/instantiators/helpers.py @@ -19,7 +19,10 @@ output_store_checks, run_on_attestation, run_on_attester_slashing, - run_on_block + run_on_block, + get_block_file_name, + get_attestation_file_name, + get_attester_slashing_file_name, ) from .debug_helpers import print_head @@ -304,6 +307,37 @@ def wrapper(*args, **kwargs): return wrapper +def _add_block(spec, store, signed_block, test_steps): + """ + Helper method to add a block, when it's unknown whether it's valid or not + """ + yield get_block_file_name(signed_block), signed_block + try: + run_on_block(spec, store, signed_block) + valid = True + except AssertionError: + valid = False + + test_steps.append({'block': get_block_file_name(signed_block), 'valid': valid}) + + if valid: + # An on_block step implies receiving block's attestations + for attestation in signed_block.message.body.attestations: + try: + run_on_attestation(spec, store, attestation, is_from_block=True, valid=True) + except AssertionError: + # ignore possible faults, if the block is valud + pass + + # An on_block step implies receiving block's attester slashings + for attester_slashing in signed_block.message.body.attester_slashings: + try: + run_on_attester_slashing(spec, store, attester_slashing, valid=True) + except AssertionError: + # ignore possible faults, if the block is valud + pass + + @filter_out_duplicate_messages def yield_fork_choice_test_events(spec, store, test_data: FCTestData, test_events: list, debug: bool): # Yield meta @@ -314,6 +348,18 @@ def yield_fork_choice_test_events(spec, store, test_data: FCTestData, test_event yield 'anchor_state', test_data.anchor_state yield 'anchor_block', test_data.anchor_block + for message in test_data.blocks: + block = message.payload + yield get_block_file_name(block), block.encode_bytes() + + for message in test_data.atts: + attestation = message.payload + yield get_attestation_file_name(attestation), attestation.encode_bytes() + + for message in test_data.slashings: + attester_slashing = message.payload + yield get_attester_slashing_file_name(attester_slashing), attester_slashing.encode_bytes() + test_steps = [] def try_add_mesage(runner, message): @@ -323,34 +369,41 @@ def try_add_mesage(runner, message): except AssertionError: return False + # record initial tick + on_tick_and_append_step(spec, store, store.time, test_steps, checks_with_viable_for_head_weights=True) + for event in test_events: event_kind = event[0] if event_kind == 'tick': _, time, _ = event if time > store.time: - on_tick_and_append_step(spec, store, time, test_steps) + on_tick_and_append_step(spec, store, time, test_steps, checks_with_viable_for_head_weights=True) assert store.time == time elif event_kind == 'block': _, signed_block, valid = event if valid is None: - valid = try_add_mesage(run_on_block, signed_block) - yield from add_block(spec, store, signed_block, test_steps, valid=valid) - - block_root = signed_block.message.hash_tree_root() - if valid: - assert store.blocks[block_root] == signed_block.message + yield from _add_block(spec, store, signed_block, test_steps) else: - assert block_root not in store.blocks.values() + yield from add_block(spec, store, signed_block, test_steps, valid=valid) + + block_root = signed_block.message.hash_tree_root() + if valid: + assert store.blocks[block_root] == signed_block.message + else: + assert block_root not in store.blocks.values() + output_store_checks(spec, store, test_steps, with_viable_for_head_weights=True) elif event_kind == 'attestation': _, attestation, valid = event if valid is None: valid = try_add_mesage(run_on_attestation, attestation) yield from add_attestation(spec, store, attestation, test_steps, valid=valid) + output_store_checks(spec, store, test_steps, with_viable_for_head_weights=True) elif event_kind == 'attester_slashing': _, attester_slashing, valid = event if valid is None: valid = try_add_mesage(run_on_attester_slashing, attester_slashing) yield from add_attester_slashing(spec, store, attester_slashing, test_steps, valid=valid) + output_store_checks(spec, store, test_steps, with_viable_for_head_weights=True) else: raise ValueError('Unknown event ' + str(event_kind)) diff --git a/tests/generators/fork_choice_generated/scheduler.py b/tests/generators/fork_choice_generated/scheduler.py index d7062ef548..fa32acaf41 100644 --- a/tests/generators/fork_choice_generated/scheduler.py +++ b/tests/generators/fork_choice_generated/scheduler.py @@ -25,9 +25,9 @@ def __init__(self, message, is_attestation, is_from_block=False): class MessageScheduler: - def __init__(self, spec, anchor_state, anchor_block): + def __init__(self, spec, store): self.spec = spec - self.store = spec.get_forkchoice_store(anchor_state, anchor_block) + self.store = store self.message_queue = [] def is_early_message(self, item: QueueItem) -> bool: @@ -42,30 +42,45 @@ def drain_queue(self, ) -> list[QueueItem]: self.message_queue.clear() return messages - def process_queue(self): + def process_queue(self) -> tuple[bool, list]: + applied_events = [] updated = False for item in self.drain_queue(): if self.is_early_message(item): self.enque_message(item) else: if item.is_attestation: - self.process_attestation(item.message) + if self.process_attestation(item.message): + applied_events.append(('attestation', item.message, True)) else: - updated |= self.process_block(item.message) - return updated + updated_, events_ = self.process_block(item.message, recovery=True) + if updated_: + updated = True + applied_events.extend(events_) + assert ('block', item.message, True) in events_ + return updated, applied_events - def purge_queue(self): - while self.process_queue(): - pass + def purge_queue(self) -> list: + applied_events = [] + while True: + updated, events = self.process_queue() + applied_events.extend(events) + if updated: + continue + else: + return applied_events - def process_tick(self, time): + def process_tick(self, time) -> list: + applied_events = [] SECONDS_PER_SLOT = self.spec.config.SECONDS_PER_SLOT assert time >= self.store.time tick_slot = (time - self.store.genesis_time) // SECONDS_PER_SLOT while self.spec.get_current_slot(self.store) < tick_slot: previous_time = self.store.genesis_time + (self.spec.get_current_slot(self.store) + 1) * SECONDS_PER_SLOT self.spec.on_tick(self.store, previous_time) - self.purge_queue() + applied_events.append(('tick', previous_time, self.spec.get_current_slot(self.store) < tick_slot)) + applied_events.extend(self.purge_queue()) + return applied_events def process_attestation(self, attestation, is_from_block=False): try: @@ -91,16 +106,18 @@ def process_block_messages(self, signed_block): for attester_slashing in block.body.attester_slashings: self.process_slashing(attester_slashing) - def process_block(self, signed_block): + def process_block(self, signed_block, recovery=False) -> tuple[bool, list]: + applied_events = [] try: self.spec.on_block(self.store, signed_block) valid = True + applied_events.append(('block', signed_block, recovery)) except AssertionError: item = QueueItem(signed_block, False) if self.is_early_message(item): self.enque_message(item) valid = False if valid: - self.purge_queue() + applied_events.extend(self.purge_queue()) self.process_block_messages(signed_block) - return valid + return valid, applied_events diff --git a/tests/generators/fork_choice_generated/test_provider.py b/tests/generators/fork_choice_generated/test_provider.py index 8c6fdc26d0..0bf1dac836 100644 --- a/tests/generators/fork_choice_generated/test_provider.py +++ b/tests/generators/fork_choice_generated/test_provider.py @@ -15,7 +15,7 @@ from instantiators.block_cover import yield_block_cover_test_case, yield_block_cover_test_data from instantiators.block_tree import yield_block_tree_test_case, yield_block_tree_test_data from instantiators.helpers import FCTestData, make_events, yield_fork_choice_test_events, filter_out_duplicate_messages -from mutation_operators import mutate_test_vector +from instantiators.mutation_operators import MutationOps import random @@ -52,13 +52,19 @@ def mutation_case_fn(self): test_data = list(self.call_instantiator(test_data_only=True))[0][2] events = make_events(spec, test_data) store = spec.get_forkchoice_store(test_data.anchor_state, test_data.anchor_block) + start_time = store.time + seconds_per_slot = spec.config.SECONDS_PER_SLOT + if mut_seed is None: return (spec_test(yield_fork_choice_test_events))( spec, store, test_data, events, self.debug, generator_mode=True, bls_active=self.bls_active) else: test_vector = events_to_test_vector(events) - mutated_vector, = list(mutate_test_vector(random.Random(mut_seed), test_vector, 1, debug=self.debug)) - #mutated_tc.meta['mutation_seed'] = mut_seed + mops = MutationOps(start_time, seconds_per_slot) + mutated_vector, mutations = mops.rand_mutations(test_vector, 4, random.Random(mut_seed)) + + test_data.meta['mut_seed'] = mut_seed + test_data.meta['mutations'] = mutations mutated_events = test_vector_to_events(mutated_vector) @@ -128,6 +134,8 @@ def test_vector_to_events(test_vector): @filter_out_duplicate_messages def yield_test_parts(spec, store, test_data: FCTestData, events): + record_recovery_messages = True + for k,v in test_data.meta.items(): yield k, 'meta', v @@ -136,35 +144,88 @@ def yield_test_parts(spec, store, test_data: FCTestData, events): for message in test_data.blocks: block = message.payload - yield get_block_file_name(block), block.encode_bytes() + yield get_block_file_name(block), block for message in test_data.atts: attestation = message.payload - yield get_attestation_file_name(attestation), attestation.encode_bytes() + yield get_attestation_file_name(attestation), attestation for message in test_data.slashings: attester_slashing = message.payload - yield get_attester_slashing_file_name(attester_slashing), attester_slashing.encode_bytes() + yield get_attester_slashing_file_name(attester_slashing), attester_slashing - anchor_state = test_data.anchor_state - anchor_block = test_data.anchor_block test_steps = [] - scheduler = MessageScheduler(spec, anchor_state, anchor_block) + scheduler = MessageScheduler(spec, store) + + # record first tick + on_tick_and_append_step(spec, store, store.time, test_steps) for (kind, data, _) in events: if kind == 'tick': time = data if time > store.time: - scheduler.process_tick(time) - on_tick_and_append_step(spec, store, time, test_steps) - - # output checks after applying buffered messages, since they affect store state - output_store_checks(spec, store, test_steps) + applied_events = scheduler.process_tick(time) + if record_recovery_messages: + for (event_kind, event_data, recovery) in applied_events: + if event_kind == 'tick': + test_steps.append({'tick': int(event_data)}) + elif event_kind == 'block': + assert recovery + _block_id = get_block_file_name(event_data) + print('recovered block', _block_id) + test_steps.append({'block': _block_id, 'valid': True, 'recovery': True}) + elif event_kind == 'attestation': + assert recovery + _attestation_id = get_attestation_file_name(event_data) + if _attestation_id not in test_data.atts: + yield _attestation_id, event_data + print('recovered attestation', _attestation_id) + test_steps.append({'attestation': _attestation_id, 'valid': True, 'recovery': True}) + else: + assert False + else: + assert False + if time > store.time: + # inside a slot + on_tick_and_append_step(spec, store, time, test_steps) + else: + assert time == store.time + output_store_checks(spec, store, test_steps) elif kind == 'block': block = data block_id = get_block_file_name(block) - valid = scheduler.process_block(block) - test_steps.append({'block': block_id, 'valid': valid}) + valid, applied_events = scheduler.process_block(block) + if record_recovery_messages: + if valid: + for (event_kind, event_data, recovery) in applied_events: + if event_kind == 'block': + _block_id = get_block_file_name(event_data) + if recovery: + print('recovered block', _block_id) + test_steps.append({'block': _block_id, 'valid': True, 'recovery': True}) + else: + test_steps.append({'block': _block_id, 'valid': True}) + elif event_kind == 'attestation': + _attestation_id = get_attestation_file_name(event_data) + if recovery: + print('recovered attestation', _attestation_id) + if _attestation_id not in test_data.atts: + yield _attestation_id, event_data + test_steps.append({'attestation': _attestation_id, 'valid': True, 'recovery': True}) + else: + assert False + test_steps.append({'attestation': _attestation_id, 'valid': True}) + else: + assert False + else: + assert len(applied_events) == 0 + test_steps.append({'block': block_id, 'valid': valid}) + else: + assert False + test_steps.append({'block': block_id, 'valid': valid}) + block_root = block.message.hash_tree_root() + assert valid == (block_root in store.blocks) + output_store_checks(spec, store, test_steps) elif kind == 'attestation': attestation = data @@ -181,8 +242,7 @@ def yield_test_parts(spec, store, test_data: FCTestData, events): else: raise ValueError(f'not implemented {kind}') next_slot_time = store.genesis_time + (spec.get_current_slot(store) + 1) * spec.config.SECONDS_PER_SLOT - # on_tick_and_append_step(spec, store, next_slot_time, test_steps, checks_with_viable_for_head_weights=True) - on_tick_and_append_step(spec, store, next_slot_time, test_steps) + on_tick_and_append_step(spec, store, next_slot_time, test_steps, checks_with_viable_for_head_weights=True) yield 'steps', test_steps From 7c9ddbb1a3e76b6af4156c6b9bb87f5df0fc7df8 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Fri, 28 Jun 2024 01:57:47 +0400 Subject: [PATCH 094/111] Move mutation_operators to instantiators --- .../{ => instantiators}/mutation_operators.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/generators/fork_choice_generated/{ => instantiators}/mutation_operators.py (100%) diff --git a/tests/generators/fork_choice_generated/mutation_operators.py b/tests/generators/fork_choice_generated/instantiators/mutation_operators.py similarity index 100% rename from tests/generators/fork_choice_generated/mutation_operators.py rename to tests/generators/fork_choice_generated/instantiators/mutation_operators.py From f184254f67ba821e569f7a3b05debada014b25d7 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Fri, 28 Jun 2024 16:44:14 +0600 Subject: [PATCH 095/111] Extend fork choice test format --- tests/formats/fork_choice/README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/formats/fork_choice/README.md b/tests/formats/fork_choice/README.md index 1258a66c06..066e4ceed5 100644 --- a/tests/formats/fork_choice/README.md +++ b/tests/formats/fork_choice/README.md @@ -189,6 +189,13 @@ should_override_forkchoice_update: { -- [New in Bellatrix] } ``` +Additional check utilized by the generated fork choice tests: +```yaml +viable_for_head_roots_and_weights: { + : -- A map listing roots of all viable for head blocks and their fork choice weights +} +``` + For example: ```yaml - checks: @@ -199,6 +206,8 @@ For example: proposer_boost_root: '0xdaa1d49d57594ced0c35688a6da133abb086d191a2ebdfd736fad95299325aeb' get_proposer_head: '0xdaa1d49d57594ced0c35688a6da133abb086d191a2ebdfd736fad95299325aeb' should_override_forkchoice_update: {validator_is_connected: false, result: false} + viable_for_head_roots_and_weights: {'0x533290b6f44d31c925acd08dfc8448624979d48c40b877d4e6714648866c9ddb': 192000000000, + '0x5cfb9d9099cdf1d8ab68ce96cdae9f0fa6eef16914a01070580dfdc1d2d59ec3': 544000000000} ``` *Note*: Each `checks` step may include one or multiple items. Each item has to be checked against the current store. From 47199a3063f2fff0217410e4ed3a0beb2ff9cd5f Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Fri, 28 Jun 2024 19:24:13 +0400 Subject: [PATCH 096/111] remove with_viable_for_head_weights=True (kept by mistake) --- .../fork_choice_generated/instantiators/helpers.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/generators/fork_choice_generated/instantiators/helpers.py b/tests/generators/fork_choice_generated/instantiators/helpers.py index 1ccb056160..cb8c2c76ff 100644 --- a/tests/generators/fork_choice_generated/instantiators/helpers.py +++ b/tests/generators/fork_choice_generated/instantiators/helpers.py @@ -370,14 +370,14 @@ def try_add_mesage(runner, message): return False # record initial tick - on_tick_and_append_step(spec, store, store.time, test_steps, checks_with_viable_for_head_weights=True) + on_tick_and_append_step(spec, store, store.time, test_steps) for event in test_events: event_kind = event[0] if event_kind == 'tick': _, time, _ = event if time > store.time: - on_tick_and_append_step(spec, store, time, test_steps, checks_with_viable_for_head_weights=True) + on_tick_and_append_step(spec, store, time, test_steps) assert store.time == time elif event_kind == 'block': _, signed_block, valid = event @@ -391,19 +391,19 @@ def try_add_mesage(runner, message): assert store.blocks[block_root] == signed_block.message else: assert block_root not in store.blocks.values() - output_store_checks(spec, store, test_steps, with_viable_for_head_weights=True) + output_store_checks(spec, store, test_steps) elif event_kind == 'attestation': _, attestation, valid = event if valid is None: valid = try_add_mesage(run_on_attestation, attestation) yield from add_attestation(spec, store, attestation, test_steps, valid=valid) - output_store_checks(spec, store, test_steps, with_viable_for_head_weights=True) + output_store_checks(spec, store, test_steps) elif event_kind == 'attester_slashing': _, attester_slashing, valid = event if valid is None: valid = try_add_mesage(run_on_attester_slashing, attester_slashing) yield from add_attester_slashing(spec, store, attester_slashing, test_steps, valid=valid) - output_store_checks(spec, store, test_steps, with_viable_for_head_weights=True) + output_store_checks(spec, store, test_steps) else: raise ValueError('Unknown event ' + str(event_kind)) From 02a02872db51b338991afdceb3aa1270a1b9a874 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Fri, 28 Jun 2024 19:27:05 +0400 Subject: [PATCH 097/111] put back MutationOps (dropped by mistake) --- .../instantiators/mutation_operators.py | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/tests/generators/fork_choice_generated/instantiators/mutation_operators.py b/tests/generators/fork_choice_generated/instantiators/mutation_operators.py index 1f68170aa7..d5fdeb6ea2 100644 --- a/tests/generators/fork_choice_generated/instantiators/mutation_operators.py +++ b/tests/generators/fork_choice_generated/instantiators/mutation_operators.py @@ -6,6 +6,8 @@ def mut_shift_(tv, idx, delta): new_time = int(time) + delta if new_time >= 0: return sorted(tv[:idx] + [(new_time, event)] + tv[idx+1:], key=lambda x: x[0]) + else: + return idx def mut_shift(tv, rnd: random.Random): @@ -71,3 +73,59 @@ def mutate_test_vector(rnd, initial_tv, cnt, debug=False): else: assert False yield tv_ + + +class MutationOps: + def __init__(self, start_time, seconds_per_slot, shift_bounds=(-2,4)): + self.start_time = int(start_time) + self.seconds_per_slot = int(seconds_per_slot) + self.shift_bounds = shift_bounds + + def apply_shift(self, tv, idx, delta): + return mut_shift_(tv, idx, delta) + + def apply_drop(self, tv, idx): + return mut_drop_(tv, idx) + + def apply_dup_shift(self, tv, idx, delta): + return mut_dup_(tv, idx, delta) + + def apply_mutation(self, tv, op_kind, *params): + if op_kind == 'shift': + return self.apply_shift(tv, *params) + elif op_kind == 'dup_shift': + return self.apply_dup_shift(tv, *params) + elif op_kind == 'drop': + return self.apply_drop(tv, *params) + else: + assert False + + def rand_shift(self, time: int, rnd: random.Random) -> int: + assert time >= self.start_time + neg_shift, pos_shift = self.shift_bounds + min_shift = max(self.start_time - time, neg_shift * self.seconds_per_slot) + max_shift = pos_shift * self.seconds_per_slot + if rnd.randint(0, 1) == 0: + return rnd.randint(min_shift, 0) + else: + return rnd.randint(1, max_shift) + + def rand_mutation(self, tv, rnd: random.Random): + idx = rnd.choice(range(len(tv))) + op_kind = rnd.choice(['shift', 'drop', 'dup_shift']) + if op_kind == 'shift' or op_kind == 'dup_shift': + evt_time = int(tv[idx][0]) + params = idx, self.rand_shift(evt_time, rnd) + else: + params = idx, + return op_kind, *params + + def rand_mutations(self, tv, num, rnd: random.Random): + mutations = [] + for _ in range(num): + if len(tv) == 0: + break + mut_op = self.rand_mutation(tv, rnd) + mutations.append(mut_op) + tv = self.apply_mutation(tv, *mut_op) + return tv, mutations From 423515efd84d8c83db4be802a499bf5b59dd0432 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Fri, 28 Jun 2024 19:36:40 +0400 Subject: [PATCH 098/111] switch to DENEB as the default fork --- tests/generators/fork_choice_generated/instance_generator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/generators/fork_choice_generated/instance_generator.py b/tests/generators/fork_choice_generated/instance_generator.py index 45387a06be..f9dd1fb335 100644 --- a/tests/generators/fork_choice_generated/instance_generator.py +++ b/tests/generators/fork_choice_generated/instance_generator.py @@ -1,4 +1,4 @@ -from eth2spec.test.helpers.constants import ALTAIR +from eth2spec.test.helpers.constants import ALTAIR, DENEB from eth2spec.gen_helpers.gen_base import gen_runner from eth2spec.test.helpers.constants import MINIMAL, MAINNET from itertools import product @@ -9,7 +9,7 @@ from test_provider import GENERATOR_NAME, create_providers -forks = [ALTAIR] +forks = [DENEB] presets = [MINIMAL] From 9930464967799db8d9e372959e9fda14822214eb Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Mon, 1 Jul 2024 04:21:57 +0400 Subject: [PATCH 099/111] remove debug prints and recovery property from test steps --- .../fork_choice_generated/test_provider.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/generators/fork_choice_generated/test_provider.py b/tests/generators/fork_choice_generated/test_provider.py index 0bf1dac836..bf614d2cc4 100644 --- a/tests/generators/fork_choice_generated/test_provider.py +++ b/tests/generators/fork_choice_generated/test_provider.py @@ -172,15 +172,15 @@ def yield_test_parts(spec, store, test_data: FCTestData, events): elif event_kind == 'block': assert recovery _block_id = get_block_file_name(event_data) - print('recovered block', _block_id) - test_steps.append({'block': _block_id, 'valid': True, 'recovery': True}) + #print('recovered block', _block_id) + test_steps.append({'block': _block_id, 'valid': True}) #, 'recovery': True}) elif event_kind == 'attestation': assert recovery _attestation_id = get_attestation_file_name(event_data) if _attestation_id not in test_data.atts: yield _attestation_id, event_data - print('recovered attestation', _attestation_id) - test_steps.append({'attestation': _attestation_id, 'valid': True, 'recovery': True}) + #print('recovered attestation', _attestation_id) + test_steps.append({'attestation': _attestation_id, 'valid': True}) #, 'recovery': True}) else: assert False else: @@ -201,17 +201,17 @@ def yield_test_parts(spec, store, test_data: FCTestData, events): if event_kind == 'block': _block_id = get_block_file_name(event_data) if recovery: - print('recovered block', _block_id) - test_steps.append({'block': _block_id, 'valid': True, 'recovery': True}) + #print('recovered block', _block_id) + test_steps.append({'block': _block_id, 'valid': True}) #, 'recovery': True}) else: test_steps.append({'block': _block_id, 'valid': True}) elif event_kind == 'attestation': _attestation_id = get_attestation_file_name(event_data) if recovery: - print('recovered attestation', _attestation_id) + #print('recovered attestation', _attestation_id) if _attestation_id not in test_data.atts: yield _attestation_id, event_data - test_steps.append({'attestation': _attestation_id, 'valid': True, 'recovery': True}) + test_steps.append({'attestation': _attestation_id, 'valid': True}) #, 'recovery': True}) else: assert False test_steps.append({'attestation': _attestation_id, 'valid': True}) From 6a27e1e4f165cab841acea96906e60dc7e314277 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Mon, 1 Jul 2024 04:24:12 +0400 Subject: [PATCH 100/111] drop unnecessary comments --- .../fork_choice_generated/test_provider.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/tests/generators/fork_choice_generated/test_provider.py b/tests/generators/fork_choice_generated/test_provider.py index bf614d2cc4..80043d6e87 100644 --- a/tests/generators/fork_choice_generated/test_provider.py +++ b/tests/generators/fork_choice_generated/test_provider.py @@ -172,15 +172,13 @@ def yield_test_parts(spec, store, test_data: FCTestData, events): elif event_kind == 'block': assert recovery _block_id = get_block_file_name(event_data) - #print('recovered block', _block_id) - test_steps.append({'block': _block_id, 'valid': True}) #, 'recovery': True}) + test_steps.append({'block': _block_id, 'valid': True}) elif event_kind == 'attestation': assert recovery _attestation_id = get_attestation_file_name(event_data) if _attestation_id not in test_data.atts: yield _attestation_id, event_data - #print('recovered attestation', _attestation_id) - test_steps.append({'attestation': _attestation_id, 'valid': True}) #, 'recovery': True}) + test_steps.append({'attestation': _attestation_id, 'valid': True}) else: assert False else: @@ -201,17 +199,15 @@ def yield_test_parts(spec, store, test_data: FCTestData, events): if event_kind == 'block': _block_id = get_block_file_name(event_data) if recovery: - #print('recovered block', _block_id) - test_steps.append({'block': _block_id, 'valid': True}) #, 'recovery': True}) + test_steps.append({'block': _block_id, 'valid': True}) else: test_steps.append({'block': _block_id, 'valid': True}) elif event_kind == 'attestation': _attestation_id = get_attestation_file_name(event_data) if recovery: - #print('recovered attestation', _attestation_id) if _attestation_id not in test_data.atts: yield _attestation_id, event_data - test_steps.append({'attestation': _attestation_id, 'valid': True}) #, 'recovery': True}) + test_steps.append({'attestation': _attestation_id, 'valid': True}) else: assert False test_steps.append({'attestation': _attestation_id, 'valid': True}) From 5a14ef6645dc081759b702685749a3aebeb414d8 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Mon, 1 Jul 2024 04:26:04 +0400 Subject: [PATCH 101/111] Drop `checks_with_viable_for_head_weights` which is not a part of `on_tick_and_append_step` yet --- tests/generators/fork_choice_generated/test_provider.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/generators/fork_choice_generated/test_provider.py b/tests/generators/fork_choice_generated/test_provider.py index 80043d6e87..006915cd63 100644 --- a/tests/generators/fork_choice_generated/test_provider.py +++ b/tests/generators/fork_choice_generated/test_provider.py @@ -238,7 +238,8 @@ def yield_test_parts(spec, store, test_data: FCTestData, events): else: raise ValueError(f'not implemented {kind}') next_slot_time = store.genesis_time + (spec.get_current_slot(store) + 1) * spec.config.SECONDS_PER_SLOT - on_tick_and_append_step(spec, store, next_slot_time, test_steps, checks_with_viable_for_head_weights=True) + on_tick_and_append_step(spec, store, next_slot_time, test_steps) + output_store_checks(spec, store, test_steps, with_viable_for_head_weights=True) yield 'steps', test_steps From 4ed16abdb461f3e0d74ce579b7f815e819f320ab Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 2 Jul 2024 00:30:30 +0400 Subject: [PATCH 102/111] added `viable_for_head_roots_and_weights` check description --- tests/formats/fork_choice/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/formats/fork_choice/README.md b/tests/formats/fork_choice/README.md index 066e4ceed5..f848fb60f0 100644 --- a/tests/formats/fork_choice/README.md +++ b/tests/formats/fork_choice/README.md @@ -177,6 +177,10 @@ finalized_checkpoint: { root: string, -- Encoded 32-byte value from store.finalized_checkpoint.root } proposer_boost_root: string -- Encoded 32-byte value from store.proposer_boost_root +viable_for_head_roots_and_weights: { + root: string, -- Encoded 32-byte value of filtered_block_tree leaf blocks + weight: int -- Integer value from get_weight(store, viable_block_root) +} ``` Additionally, these fields if `get_proposer_head` and `should_override_forkchoice_update` features are implemented: From fb3ff0fe3d632d8a4ed0ef0b2ca19b9266641d1b Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 2 Jul 2024 03:26:17 +0400 Subject: [PATCH 103/111] fixed names (small -> tiny, smoke -> small) --- .../generate_test_instances.py | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/tests/generators/fork_choice_generated/generate_test_instances.py b/tests/generators/fork_choice_generated/generate_test_instances.py index 0c997d8ce1..3846719cf5 100644 --- a/tests/generators/fork_choice_generated/generate_test_instances.py +++ b/tests/generators/fork_choice_generated/generate_test_instances.py @@ -115,26 +115,26 @@ def generate_block_cover(params): gen_params = { ################### - # small instances # + # tiny instances # ################### - 'block_tree_tree_small': { - 'out_path': 'small/block_tree_tree.yaml', + 'block_tree_tree_tiny': { + 'out_path': 'tiny/block_tree_tree.yaml', 'models': ['sm_link', 'block_tree'], 'params': [ ({'anchor_epoch': 0, 'number_of_epochs': 4, 'number_of_links': 3}, {'number_of_blocks': 8, 'max_children': 2, 'number_of_solutions': 3}), ([{'sm_links': [[0, 1], [0, 2], [2, 3], [3, 4]]}], {'number_of_blocks': 16, 'max_children': 2, 'number_of_solutions': 3}), ] }, - 'block_tree_other_small': { - 'out_path': 'small/block_tree_other.yaml', + 'block_tree_other_tiny': { + 'out_path': 'tiny/block_tree_other.yaml', 'models': ['sm_link', 'block_tree'], 'params': [ ([{'sm_links': [[0, 1], [0, 2], [2, 3], [3, 4]]}], {'number_of_blocks': 12, 'max_children': 2, 'number_of_solutions': 3}), ] }, - 'block_cover_small': { - 'out_path': 'small/block_cover.yaml', + 'block_cover_tiny': { + 'out_path': 'tiny/block_cover.yaml', 'models': ['block_cover'], 'params': [ ({'anchor_epoch': 0, 'number_of_solutions': 1},), @@ -143,11 +143,11 @@ def generate_block_cover(params): }, ################### - # smoke instances # + # small instances # ################### - 'block_tree_tree_smoke': { - 'out_path': 'smoke/block_tree_tree.yaml', + 'block_tree_tree_small': { + 'out_path': 'small/block_tree_tree.yaml', 'models': ['sm_link', 'block_tree'], 'params': [ ({'anchor_epoch': 0, 'number_of_epochs': 5, 'number_of_links': 3}, {'number_of_blocks': 16, 'max_children': 2, 'number_of_solutions': 2}), @@ -155,8 +155,8 @@ def generate_block_cover(params): ([{'sm_links': [[0, 1], [0, 2], [2, 3], [3, 4]]}], {'number_of_blocks': 12, 'max_children': 2, 'number_of_solutions': 73}), ] }, - 'block_tree_tree_smoke_2': { - 'out_path': 'smoke/block_tree_tree_2.yaml', + 'block_tree_tree_small_2': { + 'out_path': 'small/block_tree_tree_2.yaml', 'models': ['sm_link', 'block_tree'], 'params': [ ({'anchor_epoch': 0, 'number_of_epochs': 6, 'number_of_links': 4}, {'number_of_blocks': 16, 'max_children': 2, 'number_of_solutions': 2}), @@ -164,15 +164,15 @@ def generate_block_cover(params): ([{'sm_links': [[0, 1], [0, 2], [2, 3], [3, 4]]}], {'number_of_blocks': 12, 'max_children': 2, 'number_of_solutions': 283}), ] }, - 'block_tree_other_smoke': { - 'out_path': 'smoke/block_tree_other.yaml', + 'block_tree_other_small': { + 'out_path': 'small/block_tree_other.yaml', 'models': ['sm_link', 'block_tree'], 'params': [ ([{'sm_links': [[0, 1], [0, 2], [2, 3], [3, 4]]}], {'number_of_blocks': 12, 'max_children': 2, 'number_of_solutions': 4}), ] }, - 'block_cover_smoke': { - 'out_path': 'smoke/block_cover.yaml', + 'block_cover_small': { + 'out_path': 'small/block_cover.yaml', 'models': ['block_cover'], 'params': [ ({'anchor_epoch': 0, 'number_of_solutions': 2},), From c494a969edea69cd6213470cdff5b761c6ed6c1c Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 2 Jul 2024 03:26:41 +0400 Subject: [PATCH 104/111] added instructions how to generate and run tests --- .../fork_choice_generated/README.md | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/generators/fork_choice_generated/README.md b/tests/generators/fork_choice_generated/README.md index c27645d5cc..1e182d9141 100644 --- a/tests/generators/fork_choice_generated/README.md +++ b/tests/generators/fork_choice_generated/README.md @@ -7,3 +7,45 @@ Implementation of the approach described in the [Fork Choice compliance testing Preliminary research has been also performed in this [repo](https://github.com/txrx-research/fork_choice_test_generation/tree/main). To simplfy adoption of the tests, we follow the test format described in the [fork choice test formats documentation](../../formats/fork_choice/README.md), with a minor exception (new check added). + +# Pre-requisites + +Install requirements (preferrably, in a dedicated Python environment) + +''' +> pip install -r requirements.txt +''' + +In order to run tests, instal tqdm additionally. +``` +> pip install tqdm +``` + +# Generating tests + +``` +> python test_gen.py -o ${test_dir} --fc-gen-config ${config_dir}/test_gen.yaml +``` + +There are three configurations in the repo: [tiny](tiny/), [small](small/) and [standard](standard/). + +# Running tests + +Install `tqdm` library (to show progress) +``` +> pip install tqdm +``` + +and then +``` +> python test_run.py -i ${test_dirt} +``` + +# Generating configurations + +Files in [tiny](tiny/), [small](small/) and [standard](standard/) are generated with [generate_test_instances.py](generate_test_instances.py), e.g. +``` +> python generate_test_instances.py +``` + +But one normally doesn't need to generate them. \ No newline at end of file From fb231debea289f89d48e464559276bd01e3ea2ac Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 2 Jul 2024 03:28:39 +0400 Subject: [PATCH 105/111] Update README.md Fix typo --- tests/generators/fork_choice_generated/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/generators/fork_choice_generated/README.md b/tests/generators/fork_choice_generated/README.md index 1e182d9141..1d37aff1a1 100644 --- a/tests/generators/fork_choice_generated/README.md +++ b/tests/generators/fork_choice_generated/README.md @@ -12,9 +12,9 @@ To simplfy adoption of the tests, we follow the test format described in the [fo Install requirements (preferrably, in a dedicated Python environment) -''' +``` > pip install -r requirements.txt -''' +``` In order to run tests, instal tqdm additionally. ``` @@ -48,4 +48,4 @@ Files in [tiny](tiny/), [small](small/) and [standard](standard/) are generated > python generate_test_instances.py ``` -But one normally doesn't need to generate them. \ No newline at end of file +But one normally doesn't need to generate them. From eeafb1c7bfa21f23bdbcc7789f1d3c94d8b51c03 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 2 Jul 2024 03:30:14 +0400 Subject: [PATCH 106/111] Update README.md Fixed typos --- tests/generators/fork_choice_generated/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/generators/fork_choice_generated/README.md b/tests/generators/fork_choice_generated/README.md index 1d37aff1a1..e3a7d7b2f1 100644 --- a/tests/generators/fork_choice_generated/README.md +++ b/tests/generators/fork_choice_generated/README.md @@ -16,7 +16,7 @@ Install requirements (preferrably, in a dedicated Python environment) > pip install -r requirements.txt ``` -In order to run tests, instal tqdm additionally. +In order to run tests, install `tqdm` additionally. ``` > pip install tqdm ``` @@ -38,7 +38,7 @@ Install `tqdm` library (to show progress) and then ``` -> python test_run.py -i ${test_dirt} +> python test_run.py -i ${test_dir} ``` # Generating configurations From 959274fe07872814eeb263ab2f1f171c0d0b3e59 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 2 Jul 2024 17:17:51 +0400 Subject: [PATCH 107/111] Update tests/formats/fork_choice/README.md Co-authored-by: Mikhail Kalinin --- tests/formats/fork_choice/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/formats/fork_choice/README.md b/tests/formats/fork_choice/README.md index f848fb60f0..2d2a990429 100644 --- a/tests/formats/fork_choice/README.md +++ b/tests/formats/fork_choice/README.md @@ -177,10 +177,10 @@ finalized_checkpoint: { root: string, -- Encoded 32-byte value from store.finalized_checkpoint.root } proposer_boost_root: string -- Encoded 32-byte value from store.proposer_boost_root -viable_for_head_roots_and_weights: { +viable_for_head_roots_and_weights: [{ root: string, -- Encoded 32-byte value of filtered_block_tree leaf blocks weight: int -- Integer value from get_weight(store, viable_block_root) -} +}] ``` Additionally, these fields if `get_proposer_head` and `should_override_forkchoice_update` features are implemented: From ce897c86db8d6ee89842ff493cf2215b401877e0 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Tue, 2 Jul 2024 23:41:53 +0600 Subject: [PATCH 108/111] Fix viable_for_head_roots_and_weights in example --- tests/formats/fork_choice/README.md | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/tests/formats/fork_choice/README.md b/tests/formats/fork_choice/README.md index 2d2a990429..7a3aeab282 100644 --- a/tests/formats/fork_choice/README.md +++ b/tests/formats/fork_choice/README.md @@ -193,13 +193,6 @@ should_override_forkchoice_update: { -- [New in Bellatrix] } ``` -Additional check utilized by the generated fork choice tests: -```yaml -viable_for_head_roots_and_weights: { - : -- A map listing roots of all viable for head blocks and their fork choice weights -} -``` - For example: ```yaml - checks: @@ -210,8 +203,10 @@ For example: proposer_boost_root: '0xdaa1d49d57594ced0c35688a6da133abb086d191a2ebdfd736fad95299325aeb' get_proposer_head: '0xdaa1d49d57594ced0c35688a6da133abb086d191a2ebdfd736fad95299325aeb' should_override_forkchoice_update: {validator_is_connected: false, result: false} - viable_for_head_roots_and_weights: {'0x533290b6f44d31c925acd08dfc8448624979d48c40b877d4e6714648866c9ddb': 192000000000, - '0x5cfb9d9099cdf1d8ab68ce96cdae9f0fa6eef16914a01070580dfdc1d2d59ec3': 544000000000} + viable_for_head_roots_and_weights: [ + {root: '0x533290b6f44d31c925acd08dfc8448624979d48c40b877d4e6714648866c9ddb', weight: 192000000000}, + {root: '0x5cfb9d9099cdf1d8ab68ce96cdae9f0fa6eef16914a01070580dfdc1d2d59ec3', weight: 544000000000} + ] ``` *Note*: Each `checks` step may include one or multiple items. Each item has to be checked against the current store. From 543ec86bb93bc4ddf788fe7016c8b285cbb6cdef Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Wed, 3 Jul 2024 07:00:08 +0400 Subject: [PATCH 109/111] Added a line about EF grant --- tests/generators/fork_choice_generated/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/generators/fork_choice_generated/README.md b/tests/generators/fork_choice_generated/README.md index e3a7d7b2f1..39d9637cfc 100644 --- a/tests/generators/fork_choice_generated/README.md +++ b/tests/generators/fork_choice_generated/README.md @@ -8,6 +8,8 @@ Preliminary research has been also performed in this [repo](https://github.com/t To simplfy adoption of the tests, we follow the test format described in the [fork choice test formats documentation](../../formats/fork_choice/README.md), with a minor exception (new check added). +This work was supported by a grant from the Ethereum Foundation. + # Pre-requisites Install requirements (preferrably, in a dedicated Python environment) From 18542666ede19bfd403d0077345955a4617936e0 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Wed, 3 Jul 2024 07:31:59 +0400 Subject: [PATCH 110/111] Changed viable_for_head_roots_and_weights format --- tests/core/pyspec/eth2spec/test/helpers/fork_choice.py | 9 ++++++--- tests/generators/fork_choice_generated/test_run.py | 3 ++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py index 576db928db..503a8fd88f 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py +++ b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py @@ -305,10 +305,13 @@ def output_store_checks(spec, store, test_steps, with_viable_for_head_weights=Fa leaves_viable_for_head = [root for root in filtered_block_roots if not any(c for c in filtered_block_roots if store.blocks[c].parent_root == root)] - viable_for_head_roots_and_weights = { - encode_hex(viable_for_head_root): int(spec.get_weight(store, viable_for_head_root)) + viable_for_head_roots_and_weights = [ + { + 'root': encode_hex(viable_for_head_root), + 'weight': int(spec.get_weight(store, viable_for_head_root)) + } for viable_for_head_root in leaves_viable_for_head - } + ] checks['viable_for_head_roots_and_weights'] = viable_for_head_roots_and_weights test_steps.append({'checks': checks}) diff --git a/tests/generators/fork_choice_generated/test_run.py b/tests/generators/fork_choice_generated/test_run.py index 7ca9a39851..f5b09c1845 100644 --- a/tests/generators/fork_choice_generated/test_run.py +++ b/tests/generators/fork_choice_generated/test_run.py @@ -121,7 +121,8 @@ def run_test(test_info): str(viable_for_head_root): int(spec.get_weight(store, viable_for_head_root)) for viable_for_head_root in leaves_viable_for_head } - assert value == viable_for_head_roots_and_weights + expected = { kv['root']: kv['weight'] for kv in value} + assert expected == viable_for_head_roots_and_weights else: assert False else: From 2eb1575634265860b792de249d542f94de7b937e Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 14 Jan 2025 18:34:00 +0300 Subject: [PATCH 111/111] Added CLI switch to turn on multiprocessing mode --- .../generators/fork_choice_generated/test_gen.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/generators/fork_choice_generated/test_gen.py b/tests/generators/fork_choice_generated/test_gen.py index 70b363c6dc..1f925cecf5 100644 --- a/tests/generators/fork_choice_generated/test_gen.py +++ b/tests/generators/fork_choice_generated/test_gen.py @@ -1,4 +1,5 @@ from eth2spec.gen_helpers.gen_base import gen_runner +from eth2spec.gen_helpers.gen_base import settings from ruamel.yaml import YAML from instance_generator import ( @@ -70,6 +71,14 @@ def main(): required=True, help='Path to a file with test generator configurations' ) + arg_parser.add_argument( + '--fc-gen-multi-processing', + dest='fc_gen_multi_processing', + action='store_true', + default=False, + required=False, + help='If set generates tests in the multi-processing mode', + ) args = arg_parser.parse_args() @@ -77,6 +86,13 @@ def main(): yaml = YAML(typ='safe') test_gen_config = yaml.load(f) + if args.fc_gen_multi_processing: + settings.GENERATOR_MODE = settings.MODE_MULTIPROCESSING + print('generating tests in multi-processing mode') + else: + settings.GENERATOR_MODE = settings.MODE_SINGLE_PROCESS + print('generating tests in single process mode') + run_test_config(test_gen_config, debug = args.fc_gen_debug, arg_parser=arg_parser) if __name__ == "__main__":