From 9c9d41a3bc536260c803696a6489e496ad20447e Mon Sep 17 00:00:00 2001 From: WenceslasSANCHEZ Date: Wed, 23 Aug 2023 21:16:04 +0200 Subject: [PATCH 01/14] =?UTF-8?q?=E2=9C=85=20add=20multiple=20invalid=20br?= =?UTF-8?q?anch=20but=20one=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test/bellatrix/sync/test_optimistic.py | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py index eb56e368a8..3cfba6e83f 100644 --- a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py +++ b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py @@ -1,3 +1,4 @@ +from typing import Callable from eth2spec.test.context import ( spec_state_test, with_bellatrix_and_later, @@ -112,3 +113,113 @@ def test_from_syncing_to_invalid(spec, state): assert mega_store.opt_store.head_block_root == signed_blocks_a[-1].message.hash_tree_root() yield 'steps', test_steps + + +def participation_fn_gen(indices: list[int]) -> Callable: + """Generate a ``participation_fn``. + + This method must be used for the parameter `participation_fn` in + the method `state_transition_with_full_block`. + + :param indices: the validator indices that are going to attest. + :return: + """ + def participation_fn(slot, index, attestation_committee): + attestation_committee_ln = list(attestation_committee) + validator_indices = [] + for index in indices: + validator_indices.append(attestation_committee_ln[index]) + return set(validator_indices) + + return participation_fn + + +@with_bellatrix_and_later +@spec_state_test +def test_multiple_branches_sync_all_invalidated_but_one(spec, state): + test_steps = [] + # Initialization + fc_store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) + op_store = get_optimistic_store(spec, state, anchor_block) + mega_store = MegaStore(spec, fc_store, op_store) + block_hashes, signed_blocks, state_store = {}, {}, {} + yield 'anchor_state', state + yield 'anchor_block', anchor_block + + next_epoch(spec, state) + + current_time = ( + (spec.SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY * 10 + state.slot) * spec.config.SECONDS_PER_SLOT + + fc_store.genesis_time + ) + on_tick_and_append_step(spec, fc_store, current_time, test_steps) + + # Block 0 + block_0 = build_empty_block_for_next_slot(spec, state) + block_hashes['block_0'] = block_0.body.execution_payload.block_hash + signed_block = state_transition_and_sign_block(spec, state, block_0) + yield from add_optimistic_block(spec, mega_store, signed_block, test_steps, status=PayloadStatusV1Status.VALID) + assert spec.get_head(mega_store.fc_store) == mega_store.opt_store.head_block_root + + participation_indices= [ + [0, 1, 2], # chain A 3 validator attestations + [0, 1], # chain B 2 validator attestations + [0] # chain C 1 validator attestations + ] + # Create SYNC chains + state_0 = state.copy() + for j, level in enumerate(["a", "b", "c"]): + state = state_0.copy() + for i in range(3): + print(f"{level}_{i}") + block = build_empty_block_for_next_slot(spec, state) + block.body.execution_payload.parent_hash = ( + block_hashes[f'chain_{level}_{i - 1}'] if i != 0 else block_hashes['block_0'] + ) + block.body.execution_payload.extra_data = spec.hash(bytes(f'chain_{level}_{i}', 'UTF-8')) + block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) + block_hashes[f'chain_{level}_{i}'] = block.body.execution_payload.block_hash + + signed_block = state_transition_with_full_block(spec, state, True, True, block=block, participation_fn=participation_fn_gen(participation_indices[i])) + signed_blocks[f'chain_{level}_{i}'] = signed_block.copy() + yield from add_optimistic_block(spec, mega_store, signed_block, test_steps, + status=PayloadStatusV1Status.SYNCING) + assert spec.get_head(mega_store.fc_store) == mega_store.opt_store.head_block_root + state_store[level] = state.copy() + + # Check chain A is the optimistic head + assert mega_store.opt_store.head_block_root == spec.Root(signed_blocks[f'chain_a_2'].message.hash_tree_root()) + + # Add an invalid block to chain A + block = build_empty_block_for_next_slot(spec, state_store["a"]) + block.body.execution_payload.parent_hash = signed_blocks[f'chain_a_2'].message.body.execution_payload.block_hash + block.body.execution_payload.extra_data = spec.hash(bytes(f'chain_a_3', 'UTF-8')) + block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) + block_hashes['chain_a_3'] = block.body.execution_payload.block_hash + signed_block = state_transition_and_sign_block(spec, state_store["a"], block) + payload_status = PayloadStatusV1( + status=PayloadStatusV1Status.INVALID, + latest_valid_hash=block_0.body.execution_payload.block_hash, + validation_error="invalid", + ) + yield from add_optimistic_block(spec, mega_store, signed_block, test_steps, + payload_status=payload_status) + # Check chain B became the optimistic head + assert mega_store.opt_store.head_block_root == spec.Root(signed_blocks[f'chain_b_2'].message.hash_tree_root()) + + # Add an invalid block to chain B + block = build_empty_block_for_next_slot(spec, state_store["b"]) + block.body.execution_payload.parent_hash = signed_blocks[f'chain_b_2'].message.body.execution_payload.block_hash + block.body.execution_payload.extra_data = spec.hash(bytes(f'chain_b_3', 'UTF-8')) + block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) + block_hashes['chain_b_3'] = block.body.execution_payload.block_hash + signed_block = state_transition_and_sign_block(spec, state_store["b"], block) + payload_status = PayloadStatusV1( + status=PayloadStatusV1Status.INVALID, + latest_valid_hash=block_0.body.execution_payload.block_hash, + validation_error="invalid", + ) + yield from add_optimistic_block(spec, mega_store, signed_block, test_steps, + payload_status=payload_status) + # Check chain C became the optimistic head + assert mega_store.opt_store.head_block_root == spec.Root(signed_blocks[f'chain_c_2'].message.hash_tree_root()) From 126fcc88169539cef2d531f91c3542f1e9aeefcb Mon Sep 17 00:00:00 2001 From: WenceslasSANCHEZ Date: Wed, 23 Aug 2023 21:48:24 +0200 Subject: [PATCH 02/14] =?UTF-8?q?=F0=9F=90=9B=20use=20the=20index=20j=20(l?= =?UTF-8?q?inked=20to=20branch)=20instead=20of=20i=20(linkde=20to=20the=20?= =?UTF-8?q?block=20position)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py index 3cfba6e83f..f4eb2d97e2 100644 --- a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py +++ b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py @@ -179,8 +179,7 @@ def test_multiple_branches_sync_all_invalidated_but_one(spec, state): block.body.execution_payload.extra_data = spec.hash(bytes(f'chain_{level}_{i}', 'UTF-8')) block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) block_hashes[f'chain_{level}_{i}'] = block.body.execution_payload.block_hash - - signed_block = state_transition_with_full_block(spec, state, True, True, block=block, participation_fn=participation_fn_gen(participation_indices[i])) + signed_block = state_transition_with_full_block(spec, state, True, True, block=block, participation_fn=participation_fn_gen(participation_indices[j])) signed_blocks[f'chain_{level}_{i}'] = signed_block.copy() yield from add_optimistic_block(spec, mega_store, signed_block, test_steps, status=PayloadStatusV1Status.SYNCING) From b754989a4c59965624adf026be3d1665176b4c2f Mon Sep 17 00:00:00 2001 From: WenceslasSANCHEZ Date: Sun, 27 Aug 2023 17:55:41 +0200 Subject: [PATCH 03/14] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20add=20attestation=20?= =?UTF-8?q?instead=20of=20managing=20particpation=20indices?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test/bellatrix/sync/test_optimistic.py | 71 ++++++++++++------- 1 file changed, 44 insertions(+), 27 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py index f4eb2d97e2..a1b72968e1 100644 --- a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py +++ b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py @@ -1,10 +1,15 @@ -from typing import Callable from eth2spec.test.context import ( spec_state_test, with_bellatrix_and_later, + with_presets, + MINIMAL ) from eth2spec.test.helpers.attestations import ( state_transition_with_full_block, + get_valid_attestation, + build_attestation_data, + fill_aggregate_attestation, + sign_attestation, ) from eth2spec.test.helpers.block import ( build_empty_block_for_next_slot, @@ -115,26 +120,36 @@ def test_from_syncing_to_invalid(spec, state): yield 'steps', test_steps -def participation_fn_gen(indices: list[int]) -> Callable: - """Generate a ``participation_fn``. - - This method must be used for the parameter `participation_fn` in - the method `state_transition_with_full_block`. +def add_attestation_and_sign_block(spec, state, store, block, index, proportion_committee_agg: float = 1): + attestation_data = build_attestation_data( + spec, state, slot=state.slot, index=index + ) + beacon_committee = spec.get_beacon_committee( + state, + attestation_data.slot, + attestation_data.index, + ) + committee_size = len(beacon_committee) + print(beacon_committee) + number_committee_attests = int(committee_size * proportion_committee_agg) + number_committee_no_attests = committee_size - number_committee_attests + aggregation_bit_list = (*([1] * number_committee_attests), *([0] * number_committee_no_attests)) + aggregation_bits = spec.Bitlist[spec.MAX_VALIDATORS_PER_COMMITTEE](*aggregation_bit_list) + attestation = spec.Attestation( + aggregation_bits=aggregation_bits, + data=attestation_data, + ) + sign_attestation(spec, state, attestation) + spec.on_attestation(store, attestation, is_from_block=True) - :param indices: the validator indices that are going to attest. - :return: - """ - def participation_fn(slot, index, attestation_committee): - attestation_committee_ln = list(attestation_committee) - validator_indices = [] - for index in indices: - validator_indices.append(attestation_committee_ln[index]) - return set(validator_indices) + block.body.attestations.append(attestation) - return participation_fn + return state_transition_and_sign_block(spec, state, block) @with_bellatrix_and_later +@with_presets([MINIMAL], + reason="mainnet config leads to have a very small value of 'get_committee_count_per_slot'.") @spec_state_test def test_multiple_branches_sync_all_invalidated_but_one(spec, state): test_steps = [] @@ -148,8 +163,9 @@ def test_multiple_branches_sync_all_invalidated_but_one(spec, state): next_epoch(spec, state) + current_slot = spec.SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY * 10 + state.slot current_time = ( - (spec.SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY * 10 + state.slot) * spec.config.SECONDS_PER_SLOT + current_slot * spec.config.SECONDS_PER_SLOT + fc_store.genesis_time ) on_tick_and_append_step(spec, fc_store, current_time, test_steps) @@ -161,13 +177,9 @@ def test_multiple_branches_sync_all_invalidated_but_one(spec, state): yield from add_optimistic_block(spec, mega_store, signed_block, test_steps, status=PayloadStatusV1Status.VALID) assert spec.get_head(mega_store.fc_store) == mega_store.opt_store.head_block_root - participation_indices= [ - [0, 1, 2], # chain A 3 validator attestations - [0, 1], # chain B 2 validator attestations - [0] # chain C 1 validator attestations - ] # Create SYNC chains state_0 = state.copy() + participation_proportion = [1, 0.75, 0.5] for j, level in enumerate(["a", "b", "c"]): state = state_0.copy() for i in range(3): @@ -179,8 +191,13 @@ def test_multiple_branches_sync_all_invalidated_but_one(spec, state): block.body.execution_payload.extra_data = spec.hash(bytes(f'chain_{level}_{i}', 'UTF-8')) block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) block_hashes[f'chain_{level}_{i}'] = block.body.execution_payload.block_hash - signed_block = state_transition_with_full_block(spec, state, True, True, block=block, participation_fn=participation_fn_gen(participation_indices[j])) + + max_committee_index = spec.get_committee_count_per_slot(state, current_slot) + committee_index = min(j, max_committee_index-1) + signed_block = add_attestation_and_sign_block(spec, state, fc_store, block, committee_index, + proportion_committee_agg=participation_proportion[j]) signed_blocks[f'chain_{level}_{i}'] = signed_block.copy() + yield from add_optimistic_block(spec, mega_store, signed_block, test_steps, status=PayloadStatusV1Status.SYNCING) assert spec.get_head(mega_store.fc_store) == mega_store.opt_store.head_block_root @@ -189,16 +206,17 @@ def test_multiple_branches_sync_all_invalidated_but_one(spec, state): # Check chain A is the optimistic head assert mega_store.opt_store.head_block_root == spec.Root(signed_blocks[f'chain_a_2'].message.hash_tree_root()) + latest_valid_hash = block_0.body.execution_payload.block_hash + # Add an invalid block to chain A block = build_empty_block_for_next_slot(spec, state_store["a"]) block.body.execution_payload.parent_hash = signed_blocks[f'chain_a_2'].message.body.execution_payload.block_hash block.body.execution_payload.extra_data = spec.hash(bytes(f'chain_a_3', 'UTF-8')) block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) - block_hashes['chain_a_3'] = block.body.execution_payload.block_hash signed_block = state_transition_and_sign_block(spec, state_store["a"], block) payload_status = PayloadStatusV1( status=PayloadStatusV1Status.INVALID, - latest_valid_hash=block_0.body.execution_payload.block_hash, + latest_valid_hash=latest_valid_hash, validation_error="invalid", ) yield from add_optimistic_block(spec, mega_store, signed_block, test_steps, @@ -211,11 +229,10 @@ def test_multiple_branches_sync_all_invalidated_but_one(spec, state): block.body.execution_payload.parent_hash = signed_blocks[f'chain_b_2'].message.body.execution_payload.block_hash block.body.execution_payload.extra_data = spec.hash(bytes(f'chain_b_3', 'UTF-8')) block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) - block_hashes['chain_b_3'] = block.body.execution_payload.block_hash signed_block = state_transition_and_sign_block(spec, state_store["b"], block) payload_status = PayloadStatusV1( status=PayloadStatusV1Status.INVALID, - latest_valid_hash=block_0.body.execution_payload.block_hash, + latest_valid_hash=latest_valid_hash, validation_error="invalid", ) yield from add_optimistic_block(spec, mega_store, signed_block, test_steps, From 789b9d6a29be65423110c88e4fa5991db8507ee1 Mon Sep 17 00:00:00 2001 From: WenceslasSANCHEZ Date: Sun, 27 Aug 2023 18:11:58 +0200 Subject: [PATCH 04/14] =?UTF-8?q?=F0=9F=92=A1=20comment=20the=20last=20pro?= =?UTF-8?q?portion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py index a1b72968e1..0351d7b4b1 100644 --- a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py +++ b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py @@ -179,7 +179,7 @@ def test_multiple_branches_sync_all_invalidated_but_one(spec, state): # Create SYNC chains state_0 = state.copy() - participation_proportion = [1, 0.75, 0.5] + participation_proportion = [1, 0.75, 0.5] # 0.5 is useless of the branch C since ``max_committee_index=2`` for j, level in enumerate(["a", "b", "c"]): state = state_0.copy() for i in range(3): From d02df0a7a753e08f5cca24831d3ad303e2df649e Mon Sep 17 00:00:00 2001 From: WenceslasSANCHEZ Date: Sun, 27 Aug 2023 18:12:26 +0200 Subject: [PATCH 05/14] =?UTF-8?q?=F0=9F=94=A5=20remove=20test=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py index 0351d7b4b1..e5414abbb6 100644 --- a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py +++ b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py @@ -130,7 +130,6 @@ def add_attestation_and_sign_block(spec, state, store, block, index, proportion_ attestation_data.index, ) committee_size = len(beacon_committee) - print(beacon_committee) number_committee_attests = int(committee_size * proportion_committee_agg) number_committee_no_attests = committee_size - number_committee_attests aggregation_bit_list = (*([1] * number_committee_attests), *([0] * number_committee_no_attests)) @@ -183,7 +182,6 @@ def test_multiple_branches_sync_all_invalidated_but_one(spec, state): for j, level in enumerate(["a", "b", "c"]): state = state_0.copy() for i in range(3): - print(f"{level}_{i}") block = build_empty_block_for_next_slot(spec, state) block.body.execution_payload.parent_hash = ( block_hashes[f'chain_{level}_{i - 1}'] if i != 0 else block_hashes['block_0'] From d88d8203b5f2ec20ac7403f01fc0f3a1347d2868 Mon Sep 17 00:00:00 2001 From: WenceslasSANCHEZ Date: Tue, 29 Aug 2023 21:34:12 +0200 Subject: [PATCH 06/14] =?UTF-8?q?=E2=9C=85=20test=20on=20both=20minimal=20?= =?UTF-8?q?and=20mainnet=20+=20add=20equal=20weight=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test/bellatrix/sync/test_optimistic.py | 124 ++++++++++++++---- 1 file changed, 102 insertions(+), 22 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py index e5414abbb6..8a2c9eea31 100644 --- a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py +++ b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py @@ -1,14 +1,9 @@ from eth2spec.test.context import ( spec_state_test, with_bellatrix_and_later, - with_presets, - MINIMAL ) from eth2spec.test.helpers.attestations import ( - state_transition_with_full_block, - get_valid_attestation, build_attestation_data, - fill_aggregate_attestation, sign_attestation, ) from eth2spec.test.helpers.block import ( @@ -120,22 +115,14 @@ def test_from_syncing_to_invalid(spec, state): yield 'steps', test_steps -def add_attestation_and_sign_block(spec, state, store, block, index, proportion_committee_agg: float = 1): +def add_attestation_and_sign_block_with_aggregation_bit_list(spec, state, store, block, index, aggregation_bit_list): attestation_data = build_attestation_data( spec, state, slot=state.slot, index=index ) - beacon_committee = spec.get_beacon_committee( - state, - attestation_data.slot, - attestation_data.index, - ) - committee_size = len(beacon_committee) - number_committee_attests = int(committee_size * proportion_committee_agg) - number_committee_no_attests = committee_size - number_committee_attests - aggregation_bit_list = (*([1] * number_committee_attests), *([0] * number_committee_no_attests)) - aggregation_bits = spec.Bitlist[spec.MAX_VALIDATORS_PER_COMMITTEE](*aggregation_bit_list) + committee = spec.get_beacon_committee(state, attestation_data.slot, attestation_data.index) + number_empty_aggregation = len(committee) - len(aggregation_bit_list) attestation = spec.Attestation( - aggregation_bits=aggregation_bits, + aggregation_bits=spec.Bitlist[spec.MAX_VALIDATORS_PER_COMMITTEE](*(*(aggregation_bit_list), *([0]*number_empty_aggregation))), data=attestation_data, ) sign_attestation(spec, state, attestation) @@ -147,8 +134,6 @@ def add_attestation_and_sign_block(spec, state, store, block, index, proportion_ @with_bellatrix_and_later -@with_presets([MINIMAL], - reason="mainnet config leads to have a very small value of 'get_committee_count_per_slot'.") @spec_state_test def test_multiple_branches_sync_all_invalidated_but_one(spec, state): test_steps = [] @@ -178,7 +163,7 @@ def test_multiple_branches_sync_all_invalidated_but_one(spec, state): # Create SYNC chains state_0 = state.copy() - participation_proportion = [1, 0.75, 0.5] # 0.5 is useless of the branch C since ``max_committee_index=2`` + aggregation_bit_lists = [[1, 1, 1 ,1], [1, 1, 0 ,0], [0, 0, 1, 0]] for j, level in enumerate(["a", "b", "c"]): state = state_0.copy() for i in range(3): @@ -192,8 +177,9 @@ def test_multiple_branches_sync_all_invalidated_but_one(spec, state): max_committee_index = spec.get_committee_count_per_slot(state, current_slot) committee_index = min(j, max_committee_index-1) - signed_block = add_attestation_and_sign_block(spec, state, fc_store, block, committee_index, - proportion_committee_agg=participation_proportion[j]) + signed_block = add_attestation_and_sign_block_with_aggregation_bit_list( + spec, state, fc_store, block, committee_index, aggregation_bit_lists[j] + ) signed_blocks[f'chain_{level}_{i}'] = signed_block.copy() yield from add_optimistic_block(spec, mega_store, signed_block, test_steps, @@ -237,3 +223,97 @@ def test_multiple_branches_sync_all_invalidated_but_one(spec, state): payload_status=payload_status) # Check chain C became the optimistic head assert mega_store.opt_store.head_block_root == spec.Root(signed_blocks[f'chain_c_2'].message.hash_tree_root()) + + +@with_bellatrix_and_later +@spec_state_test +def test_multiple_branches_sync_all_invalidated_but_one_equal_weight(spec, state): + test_steps = [] + # Initialization + fc_store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) + op_store = get_optimistic_store(spec, state, anchor_block) + mega_store = MegaStore(spec, fc_store, op_store) + block_hashes, signed_blocks, state_store = {}, {}, {} + yield 'anchor_state', state + yield 'anchor_block', anchor_block + + next_epoch(spec, state) + + current_slot = spec.SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY * 10 + state.slot + current_time = ( + current_slot * spec.config.SECONDS_PER_SLOT + + fc_store.genesis_time + ) + on_tick_and_append_step(spec, fc_store, current_time, test_steps) + + # Block 0 + block_0 = build_empty_block_for_next_slot(spec, state) + block_hashes['block_0'] = block_0.body.execution_payload.block_hash + signed_block = state_transition_and_sign_block(spec, state, block_0) + yield from add_optimistic_block(spec, mega_store, signed_block, test_steps, status=PayloadStatusV1Status.VALID) + assert spec.get_head(mega_store.fc_store) == mega_store.opt_store.head_block_root + + # Create SYNC chains + state_0 = state.copy() + aggregation_bit_lists = [[1, 0, 0 ,0], [0, 1, 0 ,0], [0, 0, 1, 0]] + for j, level in enumerate(["a", "b", "c"]): + state = state_0.copy() + for i in range(3): + print(f"{level}_{i}") + block = build_empty_block_for_next_slot(spec, state) + block.body.execution_payload.parent_hash = ( + block_hashes[f'chain_{level}_{i - 1}'] if i != 0 else block_hashes['block_0'] + ) + block.body.execution_payload.extra_data = spec.hash(bytes(f'chain_{level}_{i}', 'UTF-8')) + block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) + block_hashes[f'chain_{level}_{i}'] = block.body.execution_payload.block_hash + + max_committee_index = spec.get_committee_count_per_slot(state, current_slot) + committee_index = min(j, max_committee_index-1) + signed_block = add_attestation_and_sign_block_with_aggregation_bit_list( + spec, state, fc_store, block, committee_index, aggregation_bit_lists[j] + ) + signed_blocks[f'chain_{level}_{i}'] = signed_block.copy() + + yield from add_optimistic_block(spec, mega_store, signed_block, test_steps, + status=PayloadStatusV1Status.SYNCING) + assert spec.get_head(mega_store.fc_store) == mega_store.opt_store.head_block_root + state_store[level] = state.copy() + + # Last added branch is considered as optimistic head + assert mega_store.opt_store.head_block_root == spec.Root(signed_blocks[f'chain_c_2'].message.hash_tree_root()) + + latest_valid_hash = block_0.body.execution_payload.block_hash + + # Add an invalid block to chain C + block = build_empty_block_for_next_slot(spec, state_store["c"]) + block.body.execution_payload.parent_hash = signed_blocks[f'chain_c_2'].message.body.execution_payload.block_hash + block.body.execution_payload.extra_data = spec.hash(bytes(f'chain_c_3', 'UTF-8')) + block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) + signed_block = state_transition_and_sign_block(spec, state_store["c"], block) + payload_status = PayloadStatusV1( + status=PayloadStatusV1Status.INVALID, + latest_valid_hash=latest_valid_hash, + validation_error="invalid", + ) + yield from add_optimistic_block(spec, mega_store, signed_block, test_steps, + payload_status=payload_status) + + assert mega_store.opt_store.head_block_root == spec.Root(signed_blocks[f'chain_b_2'].message.hash_tree_root()) + + # Add an invalid block to chain B + block = build_empty_block_for_next_slot(spec, state_store["b"]) + block.body.execution_payload.parent_hash = signed_blocks[f'chain_b_2'].message.body.execution_payload.block_hash + block.body.execution_payload.extra_data = spec.hash(bytes(f'chain_b_3', 'UTF-8')) + block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) + signed_block = state_transition_and_sign_block(spec, state_store["b"], block) + payload_status = PayloadStatusV1( + status=PayloadStatusV1Status.INVALID, + latest_valid_hash=latest_valid_hash, + validation_error="invalid", + ) + yield from add_optimistic_block(spec, mega_store, signed_block, test_steps, + payload_status=payload_status) + + # Chain A first observed (last updated) => last optimistic head + assert mega_store.opt_store.head_block_root == spec.Root(signed_blocks[f'chain_a_2'].message.hash_tree_root()) From 5134fbcf1628e1870cf80dc7c0a2d3b0311096a8 Mon Sep 17 00:00:00 2001 From: WenceslasSANCHEZ Date: Tue, 29 Aug 2023 21:42:20 +0200 Subject: [PATCH 07/14] =?UTF-8?q?=F0=9F=90=9B=20remove=20used=20import?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py index 8a2c9eea31..08f58e17e0 100644 --- a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py +++ b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py @@ -5,6 +5,7 @@ from eth2spec.test.helpers.attestations import ( build_attestation_data, sign_attestation, + state_transition_with_full_block, ) from eth2spec.test.helpers.block import ( build_empty_block_for_next_slot, From 11f777f3eea7227a9b9e0cc0afc1c02163494346 Mon Sep 17 00:00:00 2001 From: WenceslasSANCHEZ Date: Tue, 29 Aug 2023 21:46:18 +0200 Subject: [PATCH 08/14] =?UTF-8?q?=E2=9C=85=20invert=20branch=20C=20and=20B?= =?UTF-8?q?=20to=20make=20clearer=20the=20optimistic=20head=20switch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test/bellatrix/sync/test_optimistic.py | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py index 08f58e17e0..dc943b13a8 100644 --- a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py +++ b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py @@ -164,7 +164,9 @@ def test_multiple_branches_sync_all_invalidated_but_one(spec, state): # Create SYNC chains state_0 = state.copy() - aggregation_bit_lists = [[1, 1, 1 ,1], [1, 1, 0 ,0], [0, 0, 1, 0]] + # Branch A has 4 attestations, B 1 attestation and C 2 attestation + # A >> C >> B + aggregation_bit_lists = [[1, 1, 1 ,1], [1, 0, 0 ,0], [0, 1, 1, 0]] for j, level in enumerate(["a", "b", "c"]): state = state_0.copy() for i in range(3): @@ -206,15 +208,15 @@ def test_multiple_branches_sync_all_invalidated_but_one(spec, state): ) yield from add_optimistic_block(spec, mega_store, signed_block, test_steps, payload_status=payload_status) - # Check chain B became the optimistic head - assert mega_store.opt_store.head_block_root == spec.Root(signed_blocks[f'chain_b_2'].message.hash_tree_root()) + # Check chain C became the optimistic head + assert mega_store.opt_store.head_block_root == spec.Root(signed_blocks[f'chain_c_2'].message.hash_tree_root()) - # Add an invalid block to chain B - block = build_empty_block_for_next_slot(spec, state_store["b"]) - block.body.execution_payload.parent_hash = signed_blocks[f'chain_b_2'].message.body.execution_payload.block_hash - block.body.execution_payload.extra_data = spec.hash(bytes(f'chain_b_3', 'UTF-8')) + # Add an invalid block to chain C + block = build_empty_block_for_next_slot(spec, state_store["c"]) + block.body.execution_payload.parent_hash = signed_blocks[f'chain_c_2'].message.body.execution_payload.block_hash + block.body.execution_payload.extra_data = spec.hash(bytes(f'chain_c_3', 'UTF-8')) block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) - signed_block = state_transition_and_sign_block(spec, state_store["b"], block) + signed_block = state_transition_and_sign_block(spec, state_store["c"], block) payload_status = PayloadStatusV1( status=PayloadStatusV1Status.INVALID, latest_valid_hash=latest_valid_hash, @@ -222,8 +224,8 @@ def test_multiple_branches_sync_all_invalidated_but_one(spec, state): ) yield from add_optimistic_block(spec, mega_store, signed_block, test_steps, payload_status=payload_status) - # Check chain C became the optimistic head - assert mega_store.opt_store.head_block_root == spec.Root(signed_blocks[f'chain_c_2'].message.hash_tree_root()) + # Check chain B became the optimistic head + assert mega_store.opt_store.head_block_root == spec.Root(signed_blocks[f'chain_b_2'].message.hash_tree_root()) @with_bellatrix_and_later From a0bb83828ece7c601154c2a63c455a4ad7379029 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Mon, 11 Sep 2023 11:17:23 +0800 Subject: [PATCH 09/14] Make linter happy --- .../test/bellatrix/sync/test_optimistic.py | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py index dc943b13a8..f7b92739ec 100644 --- a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py +++ b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py @@ -123,7 +123,9 @@ def add_attestation_and_sign_block_with_aggregation_bit_list(spec, state, store, committee = spec.get_beacon_committee(state, attestation_data.slot, attestation_data.index) number_empty_aggregation = len(committee) - len(aggregation_bit_list) attestation = spec.Attestation( - aggregation_bits=spec.Bitlist[spec.MAX_VALIDATORS_PER_COMMITTEE](*(*(aggregation_bit_list), *([0]*number_empty_aggregation))), + aggregation_bits=spec.Bitlist[spec.MAX_VALIDATORS_PER_COMMITTEE]( + *(*(aggregation_bit_list), *([0] * number_empty_aggregation)) + ), data=attestation_data, ) sign_attestation(spec, state, attestation) @@ -166,7 +168,7 @@ def test_multiple_branches_sync_all_invalidated_but_one(spec, state): state_0 = state.copy() # Branch A has 4 attestations, B 1 attestation and C 2 attestation # A >> C >> B - aggregation_bit_lists = [[1, 1, 1 ,1], [1, 0, 0 ,0], [0, 1, 1, 0]] + aggregation_bit_lists = [[1, 1, 1, 1], [1, 0, 0, 0], [0, 1, 1, 0]] for j, level in enumerate(["a", "b", "c"]): state = state_0.copy() for i in range(3): @@ -179,7 +181,7 @@ def test_multiple_branches_sync_all_invalidated_but_one(spec, state): block_hashes[f'chain_{level}_{i}'] = block.body.execution_payload.block_hash max_committee_index = spec.get_committee_count_per_slot(state, current_slot) - committee_index = min(j, max_committee_index-1) + committee_index = min(j, max_committee_index - 1) signed_block = add_attestation_and_sign_block_with_aggregation_bit_list( spec, state, fc_store, block, committee_index, aggregation_bit_lists[j] ) @@ -191,14 +193,14 @@ def test_multiple_branches_sync_all_invalidated_but_one(spec, state): state_store[level] = state.copy() # Check chain A is the optimistic head - assert mega_store.opt_store.head_block_root == spec.Root(signed_blocks[f'chain_a_2'].message.hash_tree_root()) + assert mega_store.opt_store.head_block_root == spec.Root(signed_blocks['chain_a_2'].message.hash_tree_root()) latest_valid_hash = block_0.body.execution_payload.block_hash # Add an invalid block to chain A block = build_empty_block_for_next_slot(spec, state_store["a"]) - block.body.execution_payload.parent_hash = signed_blocks[f'chain_a_2'].message.body.execution_payload.block_hash - block.body.execution_payload.extra_data = spec.hash(bytes(f'chain_a_3', 'UTF-8')) + block.body.execution_payload.parent_hash = signed_blocks['chain_a_2'].message.body.execution_payload.block_hash + block.body.execution_payload.extra_data = spec.hash(bytes('chain_a_3', 'UTF-8')) block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) signed_block = state_transition_and_sign_block(spec, state_store["a"], block) payload_status = PayloadStatusV1( @@ -209,12 +211,12 @@ def test_multiple_branches_sync_all_invalidated_but_one(spec, state): yield from add_optimistic_block(spec, mega_store, signed_block, test_steps, payload_status=payload_status) # Check chain C became the optimistic head - assert mega_store.opt_store.head_block_root == spec.Root(signed_blocks[f'chain_c_2'].message.hash_tree_root()) + assert mega_store.opt_store.head_block_root == spec.Root(signed_blocks['chain_c_2'].message.hash_tree_root()) # Add an invalid block to chain C block = build_empty_block_for_next_slot(spec, state_store["c"]) - block.body.execution_payload.parent_hash = signed_blocks[f'chain_c_2'].message.body.execution_payload.block_hash - block.body.execution_payload.extra_data = spec.hash(bytes(f'chain_c_3', 'UTF-8')) + block.body.execution_payload.parent_hash = signed_blocks['chain_c_2'].message.body.execution_payload.block_hash + block.body.execution_payload.extra_data = spec.hash(bytes('chain_c_3', 'UTF-8')) block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) signed_block = state_transition_and_sign_block(spec, state_store["c"], block) payload_status = PayloadStatusV1( @@ -225,7 +227,7 @@ def test_multiple_branches_sync_all_invalidated_but_one(spec, state): yield from add_optimistic_block(spec, mega_store, signed_block, test_steps, payload_status=payload_status) # Check chain B became the optimistic head - assert mega_store.opt_store.head_block_root == spec.Root(signed_blocks[f'chain_b_2'].message.hash_tree_root()) + assert mega_store.opt_store.head_block_root == spec.Root(signed_blocks['chain_b_2'].message.hash_tree_root()) @with_bellatrix_and_later @@ -258,7 +260,7 @@ def test_multiple_branches_sync_all_invalidated_but_one_equal_weight(spec, state # Create SYNC chains state_0 = state.copy() - aggregation_bit_lists = [[1, 0, 0 ,0], [0, 1, 0 ,0], [0, 0, 1, 0]] + aggregation_bit_lists = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0]] for j, level in enumerate(["a", "b", "c"]): state = state_0.copy() for i in range(3): @@ -272,7 +274,7 @@ def test_multiple_branches_sync_all_invalidated_but_one_equal_weight(spec, state block_hashes[f'chain_{level}_{i}'] = block.body.execution_payload.block_hash max_committee_index = spec.get_committee_count_per_slot(state, current_slot) - committee_index = min(j, max_committee_index-1) + committee_index = min(j, max_committee_index - 1) signed_block = add_attestation_and_sign_block_with_aggregation_bit_list( spec, state, fc_store, block, committee_index, aggregation_bit_lists[j] ) @@ -284,14 +286,14 @@ def test_multiple_branches_sync_all_invalidated_but_one_equal_weight(spec, state state_store[level] = state.copy() # Last added branch is considered as optimistic head - assert mega_store.opt_store.head_block_root == spec.Root(signed_blocks[f'chain_c_2'].message.hash_tree_root()) + assert mega_store.opt_store.head_block_root == spec.Root(signed_blocks['chain_c_2'].message.hash_tree_root()) latest_valid_hash = block_0.body.execution_payload.block_hash # Add an invalid block to chain C block = build_empty_block_for_next_slot(spec, state_store["c"]) - block.body.execution_payload.parent_hash = signed_blocks[f'chain_c_2'].message.body.execution_payload.block_hash - block.body.execution_payload.extra_data = spec.hash(bytes(f'chain_c_3', 'UTF-8')) + block.body.execution_payload.parent_hash = signed_blocks['chain_c_2'].message.body.execution_payload.block_hash + block.body.execution_payload.extra_data = spec.hash(bytes('chain_c_3', 'UTF-8')) block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) signed_block = state_transition_and_sign_block(spec, state_store["c"], block) payload_status = PayloadStatusV1( @@ -302,12 +304,12 @@ def test_multiple_branches_sync_all_invalidated_but_one_equal_weight(spec, state yield from add_optimistic_block(spec, mega_store, signed_block, test_steps, payload_status=payload_status) - assert mega_store.opt_store.head_block_root == spec.Root(signed_blocks[f'chain_b_2'].message.hash_tree_root()) + assert mega_store.opt_store.head_block_root == spec.Root(signed_blocks['chain_b_2'].message.hash_tree_root()) # Add an invalid block to chain B block = build_empty_block_for_next_slot(spec, state_store["b"]) - block.body.execution_payload.parent_hash = signed_blocks[f'chain_b_2'].message.body.execution_payload.block_hash - block.body.execution_payload.extra_data = spec.hash(bytes(f'chain_b_3', 'UTF-8')) + block.body.execution_payload.parent_hash = signed_blocks['chain_b_2'].message.body.execution_payload.block_hash + block.body.execution_payload.extra_data = spec.hash(bytes('chain_b_3', 'UTF-8')) block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) signed_block = state_transition_and_sign_block(spec, state_store["b"], block) payload_status = PayloadStatusV1( @@ -319,4 +321,4 @@ def test_multiple_branches_sync_all_invalidated_but_one_equal_weight(spec, state payload_status=payload_status) # Chain A first observed (last updated) => last optimistic head - assert mega_store.opt_store.head_block_root == spec.Root(signed_blocks[f'chain_a_2'].message.hash_tree_root()) + assert mega_store.opt_store.head_block_root == spec.Root(signed_blocks['chain_a_2'].message.hash_tree_root()) From f1adc04b0b1563d863f4851da8131742f48eb3bb Mon Sep 17 00:00:00 2001 From: WenceslasSANCHEZ Date: Tue, 12 Sep 2023 22:32:09 +0200 Subject: [PATCH 10/14] =?UTF-8?q?=E2=9C=85=20add=20dynamic=20chain=20head?= =?UTF-8?q?=20check?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test/bellatrix/sync/test_optimistic.py | 141 +++++++++++++----- 1 file changed, 100 insertions(+), 41 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py index f7b92739ec..db4e0d4354 100644 --- a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py +++ b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py @@ -1,3 +1,5 @@ +from collections import defaultdict + from eth2spec.test.context import ( spec_state_test, with_bellatrix_and_later, @@ -238,87 +240,144 @@ def test_multiple_branches_sync_all_invalidated_but_one_equal_weight(spec, state fc_store, anchor_block = get_genesis_forkchoice_store_and_block(spec, state) op_store = get_optimistic_store(spec, state, anchor_block) mega_store = MegaStore(spec, fc_store, op_store) - block_hashes, signed_blocks, state_store = {}, {}, {} - yield 'anchor_state', state - yield 'anchor_block', anchor_block + block_hashes, signed_blocks, state_store, signed_blocks_message_hash = ( + {}, + {}, + {}, + defaultdict(list), + ) + yield "anchor_state", state + yield "anchor_block", anchor_block next_epoch(spec, state) current_slot = spec.SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY * 10 + state.slot - current_time = ( - current_slot * spec.config.SECONDS_PER_SLOT - + fc_store.genesis_time - ) + current_time = current_slot * spec.config.SECONDS_PER_SLOT + fc_store.genesis_time on_tick_and_append_step(spec, fc_store, current_time, test_steps) # Block 0 block_0 = build_empty_block_for_next_slot(spec, state) - block_hashes['block_0'] = block_0.body.execution_payload.block_hash + block_hashes["block_0"] = block_0.body.execution_payload.block_hash signed_block = state_transition_and_sign_block(spec, state, block_0) - yield from add_optimistic_block(spec, mega_store, signed_block, test_steps, status=PayloadStatusV1Status.VALID) + yield from add_optimistic_block( + spec, mega_store, signed_block, test_steps, status=PayloadStatusV1Status.VALID + ) assert spec.get_head(mega_store.fc_store) == mega_store.opt_store.head_block_root # Create SYNC chains state_0 = state.copy() aggregation_bit_lists = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0]] - for j, level in enumerate(["a", "b", "c"]): + branch_levels = ["a", "b", "c"] + for j, level in enumerate(branch_levels): state = state_0.copy() for i in range(3): - print(f"{level}_{i}") block = build_empty_block_for_next_slot(spec, state) block.body.execution_payload.parent_hash = ( - block_hashes[f'chain_{level}_{i - 1}'] if i != 0 else block_hashes['block_0'] + block_hashes[f"chain_{level}_{i - 1}"] + if i != 0 + else block_hashes["block_0"] ) - block.body.execution_payload.extra_data = spec.hash(bytes(f'chain_{level}_{i}', 'UTF-8')) - block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) - block_hashes[f'chain_{level}_{i}'] = block.body.execution_payload.block_hash + block.body.execution_payload.extra_data = spec.hash( + bytes(f"chain_{level}_{i}", "UTF-8") + ) + block.body.execution_payload.block_hash = compute_el_block_hash( + spec, block.body.execution_payload + ) + block_hashes[f"chain_{level}_{i}"] = block.body.execution_payload.block_hash max_committee_index = spec.get_committee_count_per_slot(state, current_slot) committee_index = min(j, max_committee_index - 1) signed_block = add_attestation_and_sign_block_with_aggregation_bit_list( spec, state, fc_store, block, committee_index, aggregation_bit_lists[j] ) - signed_blocks[f'chain_{level}_{i}'] = signed_block.copy() + signed_blocks[f"chain_{level}_{i}"] = signed_block.copy() + signed_blocks_message_hash[i].append( + spec.Root(signed_block.message.hash_tree_root()) + ) - yield from add_optimistic_block(spec, mega_store, signed_block, test_steps, - status=PayloadStatusV1Status.SYNCING) - assert spec.get_head(mega_store.fc_store) == mega_store.opt_store.head_block_root + yield from add_optimistic_block( + spec, + mega_store, + signed_block, + test_steps, + status=PayloadStatusV1Status.SYNCING, + ) + assert ( + spec.get_head(mega_store.fc_store) + == mega_store.opt_store.head_block_root + ) state_store[level] = state.copy() - # Last added branch is considered as optimistic head - assert mega_store.opt_store.head_block_root == spec.Root(signed_blocks['chain_c_2'].message.hash_tree_root()) + # Since there is a weight equality, the fork head decision is done regarding the hash value + # of a signed block. + # Also, since each fork/preset can lead to a different hash, we need to know which branch has the + # highest root value to make the below assertion. + first_hash_levels = signed_blocks_message_hash[0] + optimistic_head_level = first_hash_levels.index(max(first_hash_levels)) + optimistic_head_branch = branch_levels[optimistic_head_level] + assert mega_store.opt_store.head_block_root == spec.Root( + signed_blocks[f"chain_{optimistic_head_branch}_2"].message.hash_tree_root() + ) + first_hash_levels[optimistic_head_level] = spec.Root() latest_valid_hash = block_0.body.execution_payload.block_hash - # Add an invalid block to chain C - block = build_empty_block_for_next_slot(spec, state_store["c"]) - block.body.execution_payload.parent_hash = signed_blocks['chain_c_2'].message.body.execution_payload.block_hash - block.body.execution_payload.extra_data = spec.hash(bytes('chain_c_3', 'UTF-8')) - block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) - signed_block = state_transition_and_sign_block(spec, state_store["c"], block) + # Add an invalid block to head chain + block = build_empty_block_for_next_slot(spec, state_store[optimistic_head_branch]) + block.body.execution_payload.parent_hash = signed_blocks[ + f"chain_{optimistic_head_branch}_2" + ].message.body.execution_payload.block_hash + block.body.execution_payload.extra_data = spec.hash( + bytes(f"chain_{optimistic_head_branch}_3", "UTF-8") + ) + block.body.execution_payload.block_hash = compute_el_block_hash( + spec, block.body.execution_payload + ) + signed_block = state_transition_and_sign_block( + spec, state_store[optimistic_head_branch], block + ) payload_status = PayloadStatusV1( status=PayloadStatusV1Status.INVALID, latest_valid_hash=latest_valid_hash, validation_error="invalid", ) - yield from add_optimistic_block(spec, mega_store, signed_block, test_steps, - payload_status=payload_status) - - assert mega_store.opt_store.head_block_root == spec.Root(signed_blocks['chain_b_2'].message.hash_tree_root()) + yield from add_optimistic_block( + spec, mega_store, signed_block, test_steps, payload_status=payload_status + ) - # Add an invalid block to chain B - block = build_empty_block_for_next_slot(spec, state_store["b"]) - block.body.execution_payload.parent_hash = signed_blocks['chain_b_2'].message.body.execution_payload.block_hash - block.body.execution_payload.extra_data = spec.hash(bytes('chain_b_3', 'UTF-8')) - block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload) - signed_block = state_transition_and_sign_block(spec, state_store["b"], block) + optimistic_head_level = first_hash_levels.index(max(first_hash_levels)) + optimistic_head_branch = branch_levels[optimistic_head_level] + assert mega_store.opt_store.head_block_root == spec.Root( + signed_blocks[f"chain_{optimistic_head_branch}_2"].message.hash_tree_root() + ) + first_hash_levels[optimistic_head_level] = spec.Root() + + # Add an invalid block to head chain + block = build_empty_block_for_next_slot(spec, state_store[optimistic_head_branch]) + block.body.execution_payload.parent_hash = signed_blocks[ + f"chain_{optimistic_head_branch}_2" + ].message.body.execution_payload.block_hash + block.body.execution_payload.extra_data = spec.hash( + bytes(f"chain_{optimistic_head_branch}_3", "UTF-8") + ) + block.body.execution_payload.block_hash = compute_el_block_hash( + spec, block.body.execution_payload + ) + signed_block = state_transition_and_sign_block( + spec, state_store[optimistic_head_branch], block + ) payload_status = PayloadStatusV1( status=PayloadStatusV1Status.INVALID, latest_valid_hash=latest_valid_hash, validation_error="invalid", ) - yield from add_optimistic_block(spec, mega_store, signed_block, test_steps, - payload_status=payload_status) + yield from add_optimistic_block( + spec, mega_store, signed_block, test_steps, payload_status=payload_status + ) - # Chain A first observed (last updated) => last optimistic head - assert mega_store.opt_store.head_block_root == spec.Root(signed_blocks['chain_a_2'].message.hash_tree_root()) + optimistic_head_level = first_hash_levels.index(max(first_hash_levels)) + assert mega_store.opt_store.head_block_root == spec.Root( + signed_blocks[ + f"chain_{branch_levels[optimistic_head_level]}_2" + ].message.hash_tree_root() + ) From 647d0189900fdb5e40945803a1d07d0b55f54dd3 Mon Sep 17 00:00:00 2001 From: WenceslasSANCHEZ Date: Mon, 18 Sep 2023 15:31:23 +0200 Subject: [PATCH 11/14] =?UTF-8?q?=F0=9F=94=A7=20find=20attestation=20bit?= =?UTF-8?q?=20list=20configuration=20that=20match=20both=20mainnet=20and?= =?UTF-8?q?=20minimal=20spec?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py index db4e0d4354..7b67370fbd 100644 --- a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py +++ b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py @@ -170,7 +170,7 @@ def test_multiple_branches_sync_all_invalidated_but_one(spec, state): state_0 = state.copy() # Branch A has 4 attestations, B 1 attestation and C 2 attestation # A >> C >> B - aggregation_bit_lists = [[1, 1, 1, 1], [1, 0, 0, 0], [0, 1, 1, 0]] + aggregation_bit_lists = [[1, 1, 1, 0], [1, 0, 0, 0], [0, 0, 1, 1]] for j, level in enumerate(["a", "b", "c"]): state = state_0.copy() for i in range(3): From 45873fb8d52f0075ddb67b50407600ab4e867c31 Mon Sep 17 00:00:00 2001 From: WenceslasSANCHEZ Date: Mon, 18 Sep 2023 15:31:49 +0200 Subject: [PATCH 12/14] =?UTF-8?q?=F0=9F=8E=A8=20rename=20method?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../eth2spec/test/bellatrix/sync/test_optimistic.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py index 7b67370fbd..507c60613e 100644 --- a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py +++ b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py @@ -118,7 +118,7 @@ def test_from_syncing_to_invalid(spec, state): yield 'steps', test_steps -def add_attestation_and_sign_block_with_aggregation_bit_list(spec, state, store, block, index, aggregation_bit_list): +def sign_block_with_aggregation_bit_list(spec, state, block, index, aggregation_bit_list): attestation_data = build_attestation_data( spec, state, slot=state.slot, index=index ) @@ -184,8 +184,8 @@ def test_multiple_branches_sync_all_invalidated_but_one(spec, state): max_committee_index = spec.get_committee_count_per_slot(state, current_slot) committee_index = min(j, max_committee_index - 1) - signed_block = add_attestation_and_sign_block_with_aggregation_bit_list( - spec, state, fc_store, block, committee_index, aggregation_bit_lists[j] + signed_block = sign_block_with_aggregation_bit_list( + spec, state, block, committee_index, aggregation_bit_lists[j] ) signed_blocks[f'chain_{level}_{i}'] = signed_block.copy() @@ -287,8 +287,8 @@ def test_multiple_branches_sync_all_invalidated_but_one_equal_weight(spec, state max_committee_index = spec.get_committee_count_per_slot(state, current_slot) committee_index = min(j, max_committee_index - 1) - signed_block = add_attestation_and_sign_block_with_aggregation_bit_list( - spec, state, fc_store, block, committee_index, aggregation_bit_lists[j] + signed_block = sign_block_with_aggregation_bit_list( + spec, state, block, committee_index, aggregation_bit_lists[j] ) signed_blocks[f"chain_{level}_{i}"] = signed_block.copy() signed_blocks_message_hash[i].append( From 88f43257ee67375b749356f8bacfac47f3eb3a6e Mon Sep 17 00:00:00 2001 From: WenceslasSANCHEZ Date: Mon, 18 Sep 2023 15:33:08 +0200 Subject: [PATCH 13/14] =?UTF-8?q?=F0=9F=94=A5=20useless=20usage=20of=20`on?= =?UTF-8?q?=5Fattestation`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py index 507c60613e..775899e3f1 100644 --- a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py +++ b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py @@ -131,7 +131,6 @@ def sign_block_with_aggregation_bit_list(spec, state, block, index, aggregation_ data=attestation_data, ) sign_attestation(spec, state, attestation) - spec.on_attestation(store, attestation, is_from_block=True) block.body.attestations.append(attestation) From 0273ffa4e6d6396a5d13b6326db1a714998a3fc3 Mon Sep 17 00:00:00 2001 From: WenceslasSANCHEZ Date: Mon, 18 Sep 2023 18:01:17 +0200 Subject: [PATCH 14/14] =?UTF-8?q?=F0=9F=9A=9A=20move=20``sign=5Fblock=5Fwi?= =?UTF-8?q?th=5Faggregation=5Fbit=5Flist``=20to=20``fork=5Fchoice.py``?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test/bellatrix/sync/test_optimistic.py | 26 ++----------------- .../eth2spec/test/helpers/fork_choice.py | 22 ++++++++++++++++ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py index 775899e3f1..77fbe7d6e5 100644 --- a/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py +++ b/tests/core/pyspec/eth2spec/test/bellatrix/sync/test_optimistic.py @@ -4,11 +4,7 @@ spec_state_test, with_bellatrix_and_later, ) -from eth2spec.test.helpers.attestations import ( - build_attestation_data, - sign_attestation, - state_transition_with_full_block, -) +from eth2spec.test.helpers.attestations import state_transition_with_full_block from eth2spec.test.helpers.block import ( build_empty_block_for_next_slot, ) @@ -18,6 +14,7 @@ from eth2spec.test.helpers.fork_choice import ( get_genesis_forkchoice_store_and_block, on_tick_and_append_step, + sign_block_with_aggregation_bit_list, ) from eth2spec.test.helpers.optimistic_sync import ( PayloadStatusV1, @@ -118,25 +115,6 @@ def test_from_syncing_to_invalid(spec, state): yield 'steps', test_steps -def sign_block_with_aggregation_bit_list(spec, state, block, index, aggregation_bit_list): - attestation_data = build_attestation_data( - spec, state, slot=state.slot, index=index - ) - committee = spec.get_beacon_committee(state, attestation_data.slot, attestation_data.index) - number_empty_aggregation = len(committee) - len(aggregation_bit_list) - attestation = spec.Attestation( - aggregation_bits=spec.Bitlist[spec.MAX_VALIDATORS_PER_COMMITTEE]( - *(*(aggregation_bit_list), *([0] * number_empty_aggregation)) - ), - data=attestation_data, - ) - sign_attestation(spec, state, attestation) - - block.body.attestations.append(attestation) - - return state_transition_and_sign_block(spec, state, block) - - @with_bellatrix_and_later @spec_state_test def test_multiple_branches_sync_all_invalidated_but_one(spec, state): diff --git a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py index e0e3547222..2900eda360 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py +++ b/tests/core/pyspec/eth2spec/test/helpers/fork_choice.py @@ -6,7 +6,10 @@ next_epoch_with_attestations, next_slots_with_attestations, state_transition_with_full_block, + build_attestation_data, + sign_attestation, ) +from eth2spec.test.helpers.state import state_transition_and_sign_block class BlobData(NamedTuple): @@ -388,3 +391,22 @@ def get_pow_block_file_name(pow_block): def add_pow_block(spec, store, pow_block, test_steps): yield get_pow_block_file_name(pow_block), pow_block test_steps.append({'pow_block': get_pow_block_file_name(pow_block)}) + + +def sign_block_with_aggregation_bit_list(spec, state, block, index, aggregation_bit_list): + attestation_data = build_attestation_data( + spec, state, slot=state.slot, index=index + ) + committee = spec.get_beacon_committee(state, attestation_data.slot, attestation_data.index) + number_empty_aggregation = len(committee) - len(aggregation_bit_list) + attestation = spec.Attestation( + aggregation_bits=spec.Bitlist[spec.MAX_VALIDATORS_PER_COMMITTEE]( + *(*(aggregation_bit_list), *([0] * number_empty_aggregation)) + ), + data=attestation_data, + ) + sign_attestation(spec, state, attestation) + + block.body.attestations.append(attestation) + + return state_transition_and_sign_block(spec, state, block)