From b133dedeaf9d48695d407d9800a49d3b886ba8aa Mon Sep 17 00:00:00 2001 From: protolambda Date: Wed, 26 Jun 2019 22:11:40 +0200 Subject: [PATCH 01/47] Eth1 data test --- .../eth2spec/test/sanity/test_blocks.py | 58 +++++++++++-------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py index 286c0150c..27f0a884f 100644 --- a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py +++ b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py @@ -5,7 +5,7 @@ from eth2spec.utils.bls import bls_sign from eth2spec.test.helpers.state import get_balance, state_transition_and_sign_block # from eth2spec.test.helpers.transfers import get_valid_transfer -from eth2spec.test.helpers.block import build_empty_block_for_next_slot, sign_block +from eth2spec.test.helpers.block import build_empty_block_for_next_slot, build_empty_block, sign_block from eth2spec.test.helpers.keys import privkeys, pubkeys from eth2spec.test.helpers.attester_slashings import get_valid_attester_slashing from eth2spec.test.helpers.proposer_slashings import get_valid_proposer_slashing @@ -172,8 +172,6 @@ def test_attester_slashing(spec, state): ) -# TODO update functions below to be like above, i.e. with @spec_state_test and yielding data to put into the test vector - @with_all_phases @spec_state_test def test_deposit_in_block(spec, state): @@ -386,29 +384,43 @@ def test_historical_batch(spec, state): assert len(state.historical_roots) == pre_historical_roots_len + 1 -# @with_all_phases -# @spec_state_test -# def test_eth1_data_votes(spec, state): -# yield 'pre', state +@with_all_phases +@spec_state_test +def test_eth1_data_votes_success(spec, state): + # Don't run when it will take very, very long to simulate. Minimal configuration suffices. + if spec.SLOTS_PER_ETH1_VOTING_PERIOD > 16: + return -# expected_votes = 0 -# assert len(state.eth1_data_votes) == expected_votes + offset_block = build_empty_block(spec, state, slot=spec.SLOTS_PER_ETH1_VOTING_PERIOD - 1) + state_transition_and_sign_block(spec, state, offset_block) + yield 'pre', state -# blocks = [] -# for _ in range(spec.SLOTS_PER_ETH1_VOTING_PERIOD - 1): -# block = build_empty_block_for_next_slot(spec, state) -# state_transition_and_sign_block(spec, state, block) -# expected_votes += 1 -# assert len(state.eth1_data_votes) == expected_votes -# blocks.append(block) + a = b'\xaa' * 32 + b = b'\xbb' * 32 + c = b'\xcc' * 32 -# block = build_empty_block_for_next_slot(spec, state) -# blocks.append(block) + blocks = [] -# state_transition_and_sign_block(spec, state, block) + for i in range(0, spec.SLOTS_PER_ETH1_VOTING_PERIOD): + block = build_empty_block_for_next_slot(spec, state) + # wait for over 50% for A, then start voting B + block.body.eth1_data.block_hash = b if i * 2 > spec.SLOTS_PER_ETH1_VOTING_PERIOD else a + state_transition_and_sign_block(spec, state, block) + blocks.append(block) -# yield 'blocks', [block] -# yield 'post', state + assert len(state.eth1_data_votes) == spec.SLOTS_PER_ETH1_VOTING_PERIOD + assert state.eth1_data.block_hash == a -# assert state.slot % spec.SLOTS_PER_ETH1_VOTING_PERIOD == 0 -# assert len(state.eth1_data_votes) == 1 + # transition to next eth1 voting period + block = build_empty_block_for_next_slot(spec, state) + block.body.eth1_data.block_hash = c + state_transition_and_sign_block(spec, state, block) + blocks.append(block) + + yield 'blocks', blocks + yield 'post', state + + assert state.eth1_data.block_hash == a + assert state.slot % spec.SLOTS_PER_ETH1_VOTING_PERIOD == 0 + assert len(state.eth1_data_votes) == 1 + assert state.eth1_data_votes[0].block_hash == c From f54d1a56f7192be254a8d8d87b8f67a84758af7f Mon Sep 17 00:00:00 2001 From: protolambda Date: Sat, 22 Jun 2019 01:23:37 +0200 Subject: [PATCH 02/47] eth1 voting no consensus test --- .../eth2spec/test/sanity/test_blocks.py | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py index 27f0a884f..b11e41d66 100644 --- a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py +++ b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py @@ -4,7 +4,6 @@ from eth2spec.utils.ssz.ssz_impl import signing_root from eth2spec.utils.bls import bls_sign from eth2spec.test.helpers.state import get_balance, state_transition_and_sign_block -# from eth2spec.test.helpers.transfers import get_valid_transfer from eth2spec.test.helpers.block import build_empty_block_for_next_slot, build_empty_block, sign_block from eth2spec.test.helpers.keys import privkeys, pubkeys from eth2spec.test.helpers.attester_slashings import get_valid_attester_slashing @@ -386,7 +385,7 @@ def test_historical_batch(spec, state): @with_all_phases @spec_state_test -def test_eth1_data_votes_success(spec, state): +def test_eth1_data_votes_consensus(spec, state): # Don't run when it will take very, very long to simulate. Minimal configuration suffices. if spec.SLOTS_PER_ETH1_VOTING_PERIOD > 16: return @@ -424,3 +423,33 @@ def test_eth1_data_votes_success(spec, state): assert state.slot % spec.SLOTS_PER_ETH1_VOTING_PERIOD == 0 assert len(state.eth1_data_votes) == 1 assert state.eth1_data_votes[0].block_hash == c + + +@with_all_phases +@spec_state_test +def test_eth1_data_votes_no_consensus(spec, state): + # Don't run when it will take very, very long to simulate. Minimal configuration suffices. + if spec.SLOTS_PER_ETH1_VOTING_PERIOD > 16: + return + + offset_block = build_empty_block(spec, state, slot=spec.SLOTS_PER_ETH1_VOTING_PERIOD - 1) + state_transition_and_sign_block(spec, state, offset_block) + yield 'pre', state + + a = b'\xaa' * 32 + b = b'\xbb' * 32 + + blocks = [] + + for i in range(0, spec.SLOTS_PER_ETH1_VOTING_PERIOD): + block = build_empty_block_for_next_slot(spec, state) + # wait for precisely 50% for A, then start voting B for other 50% + block.body.eth1_data.block_hash = b if i * 2 >= spec.SLOTS_PER_ETH1_VOTING_PERIOD else a + state_transition_and_sign_block(spec, state, block) + blocks.append(block) + + assert len(state.eth1_data_votes) == spec.SLOTS_PER_ETH1_VOTING_PERIOD + assert state.eth1_data.block_hash == b'\x00' * 32 + + yield 'blocks', blocks + yield 'post', state From 13b67b4cde6ddb762927ee78645ce38bd8538a76 Mon Sep 17 00:00:00 2001 From: protolambda Date: Sat, 22 Jun 2019 01:46:26 +0200 Subject: [PATCH 03/47] sign blocks in eth1 vote tests --- test_libs/pyspec/eth2spec/test/sanity/test_blocks.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py index b11e41d66..34409986e 100644 --- a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py +++ b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py @@ -373,6 +373,7 @@ def test_historical_batch(spec, state): yield 'pre', state block = build_empty_block_for_next_slot(spec, state, signed=True) + sign_block(spec, state, block) state_transition_and_sign_block(spec, state, block) yield 'blocks', [block] @@ -391,6 +392,7 @@ def test_eth1_data_votes_consensus(spec, state): return offset_block = build_empty_block(spec, state, slot=spec.SLOTS_PER_ETH1_VOTING_PERIOD - 1) + sign_block(spec, state, offset_block) state_transition_and_sign_block(spec, state, offset_block) yield 'pre', state @@ -404,6 +406,7 @@ def test_eth1_data_votes_consensus(spec, state): block = build_empty_block_for_next_slot(spec, state) # wait for over 50% for A, then start voting B block.body.eth1_data.block_hash = b if i * 2 > spec.SLOTS_PER_ETH1_VOTING_PERIOD else a + sign_block(spec, state, block) state_transition_and_sign_block(spec, state, block) blocks.append(block) @@ -413,6 +416,7 @@ def test_eth1_data_votes_consensus(spec, state): # transition to next eth1 voting period block = build_empty_block_for_next_slot(spec, state) block.body.eth1_data.block_hash = c + sign_block(spec, state, block) state_transition_and_sign_block(spec, state, block) blocks.append(block) @@ -433,6 +437,7 @@ def test_eth1_data_votes_no_consensus(spec, state): return offset_block = build_empty_block(spec, state, slot=spec.SLOTS_PER_ETH1_VOTING_PERIOD - 1) + sign_block(spec, state, offset_block) state_transition_and_sign_block(spec, state, offset_block) yield 'pre', state @@ -445,6 +450,7 @@ def test_eth1_data_votes_no_consensus(spec, state): block = build_empty_block_for_next_slot(spec, state) # wait for precisely 50% for A, then start voting B for other 50% block.body.eth1_data.block_hash = b if i * 2 >= spec.SLOTS_PER_ETH1_VOTING_PERIOD else a + sign_block(spec, state, block) state_transition_and_sign_block(spec, state, block) blocks.append(block) From 327953852d2034ebf6acf7eb7d39af84314b91b2 Mon Sep 17 00:00:00 2001 From: protolambda Date: Sat, 22 Jun 2019 01:47:53 +0200 Subject: [PATCH 04/47] test invalid shard in attestation --- .../block_processing/test_process_attestation.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py index 2b34ab405..79af0b202 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py @@ -148,6 +148,20 @@ def test_wrong_shard(spec, state): yield from run_attestation_processing(spec, state, attestation, False) +@with_all_phases +@spec_state_test +def test_invalid_shard(spec, state): + attestation = get_valid_attestation(spec, state) + state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY + + # off by one (with respect to valid range) on purpose + attestation.data.crosslink.shard = spec.SHARD_COUNT + + sign_attestation(spec, state, attestation) + + yield from run_attestation_processing(spec, state, attestation, False) + + @with_all_phases @spec_state_test def test_new_source_epoch(spec, state): From f75e3dccb2041cec7884266a7a348decd1051074 Mon Sep 17 00:00:00 2001 From: protolambda Date: Sat, 22 Jun 2019 02:03:43 +0200 Subject: [PATCH 05/47] test old and future target epoch in attestation --- .../test_process_attestation.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py index 79af0b202..59e99ac0c 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py @@ -162,6 +162,33 @@ def test_invalid_shard(spec, state): yield from run_attestation_processing(spec, state, attestation, False) +@with_all_phases +@spec_state_test +def test_old_target_epoch(spec, state): + assert spec.MIN_ATTESTATION_INCLUSION_DELAY < spec.SLOTS_PER_EPOCH * 2 + + attestation = get_valid_attestation(spec, state, signed=True) + + state.slot = spec.SLOTS_PER_EPOCH * 2 # target epoch will be too old to handle + + yield from run_attestation_processing(spec, state, attestation, False) + + +@with_all_phases +@spec_state_test +def test_future_target_epoch(spec, state): + assert spec.MIN_ATTESTATION_INCLUSION_DELAY < spec.SLOTS_PER_EPOCH * 2 + + attestation = get_valid_attestation(spec, state) + + state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY + + attestation.data.target_epoch = spec.get_current_epoch(state) + 1 # target epoch will be too new to handle + sign_attestation(spec, state, attestation) + + yield from run_attestation_processing(spec, state, attestation, False) + + @with_all_phases @spec_state_test def test_new_source_epoch(spec, state): From 64e15c524b569ce474406e1c6781d95495bcd53e Mon Sep 17 00:00:00 2001 From: protolambda Date: Sat, 22 Jun 2019 02:27:28 +0200 Subject: [PATCH 06/47] improve intersection test, just 1 index is enough. And add invalid att1/att2 tests --- .../test_process_attester_slashing.py | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py index c51f5a8a9..2d8f9d31a 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py @@ -142,12 +142,39 @@ def test_participants_already_slashed(spec, state): @with_all_phases @spec_state_test -def test_custody_bit_0_and_1(spec, state): +def test_custody_bit_0_and_1_intersect(spec, state): attester_slashing = get_valid_attester_slashing(spec, state, signed_1=False, signed_2=True) - attester_slashing.attestation_1.custody_bit_1_indices = ( - attester_slashing.attestation_1.custody_bit_0_indices + attester_slashing.attestation_1.custody_bit_1_indices.append( + attester_slashing.attestation_1.custody_bit_0_indices[0] ) + sign_indexed_attestation(spec, state, attester_slashing.attestation_1) yield from run_attester_slashing_processing(spec, state, attester_slashing, False) + + +@with_all_phases +@spec_state_test +def test_attester_slashing_invalid_att_1(spec, state): + attester_slashing = get_valid_attester_slashing(spec, state, signed_1=False, signed_2=True) + + indices = attester_slashing.attestation_1.custody_bit_0_indices + assert len(indices) >= 3 + indices[1], indices[2] = indices[2], indices[1] # unsort second and third index + sign_indexed_attestation(spec, state, attester_slashing.attestation_1) + + yield from run_attester_slashing_processing(spec, state, attester_slashing, False) + + +@with_all_phases +@spec_state_test +def test_attester_slashing_invalid_att_2(spec, state): + attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=False) + + indices = attester_slashing.attestation_2.custody_bit_0_indices + assert len(indices) >= 3 + indices[1], indices[2] = indices[2], indices[1] # unsort second and third index + sign_indexed_attestation(spec, state, attester_slashing.attestation_2) + + yield from run_attester_slashing_processing(spec, state, attester_slashing, False) From 1d6b1cab13dc979f15e1e1bc5b50ae3c3585e795 Mon Sep 17 00:00:00 2001 From: protolambda Date: Sat, 22 Jun 2019 02:37:20 +0200 Subject: [PATCH 07/47] expected deposit count test --- .../eth2spec/test/sanity/test_blocks.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py index 34409986e..bb6a6dc06 100644 --- a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py +++ b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py @@ -171,6 +171,28 @@ def test_attester_slashing(spec, state): ) +@with_all_phases +@spec_state_test +def test_expected_deposit_in_block(spec, state): + # Make the state expect a deposit, then don't provide it. + state.eth1_data.deposit_count += 1 + yield 'pre', state + + block = build_empty_block_for_next_slot(spec, state) + sign_block(spec, state, block) + bad = False + try: + state_transition_and_sign_block(spec, state, block) + bad = True + except AssertionError: + pass + if bad: + raise AssertionError("expected deposit was not enforced") + + yield 'blocks', [block] + yield 'post', None + + @with_all_phases @spec_state_test def test_deposit_in_block(spec, state): From 55d86b4f13afacf5fec4e48cb544cfd003c7cd31 Mon Sep 17 00:00:00 2001 From: protolambda Date: Sat, 22 Jun 2019 02:48:40 +0200 Subject: [PATCH 08/47] effective balance testing in deposits --- .../block_processing/test_process_deposit.py | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_deposit.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_deposit.py index 8b3d7b413..1d4b3a107 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_deposit.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_deposit.py @@ -49,6 +49,11 @@ def run_deposit_processing(spec, state, deposit, validator_index, valid=True, ef assert len(state.balances) == pre_validator_count + 1 assert get_balance(state, validator_index) == pre_balance + deposit.data.amount + effective = min(spec.MAX_EFFECTIVE_BALANCE, + pre_balance + deposit.data.amount) + effective -= effective % spec.EFFECTIVE_BALANCE_INCREMENT + assert state.validators[validator_index].effective_balance == effective + assert state.eth1_deposit_index == state.eth1_data.deposit_count @@ -57,7 +62,20 @@ def run_deposit_processing(spec, state, deposit, validator_index, valid=True, ef def test_new_deposit(spec, state): # fresh deposit = next validator index = validator appended to registry validator_index = len(state.validators) - amount = spec.MAX_EFFECTIVE_BALANCE + # effective balance will be 1 EFFECTIVE_BALANCE_INCREMENT smaller because of this small decrement. + amount = spec.MAX_EFFECTIVE_BALANCE - 1 + deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True) + + yield from run_deposit_processing(spec, state, deposit, validator_index) + + +@with_all_phases +@spec_state_test +def test_new_deposit_maxed_out(spec, state): + # fresh deposit = next validator index = validator appended to registry + validator_index = len(state.validators) + # just 1 over the limit, effective balance should be set MAX_EFFECTIVE_BALANCE during processing + amount = spec.MAX_EFFECTIVE_BALANCE + 1 deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True) yield from run_deposit_processing(spec, state, deposit, validator_index) From 063d94b9c7da65d076f563e243584fa0b8b9ab3d Mon Sep 17 00:00:00 2001 From: protolambda Date: Tue, 25 Jun 2019 04:13:48 +0200 Subject: [PATCH 09/47] Bugfix transfer tests --- .../block_processing/test_process_transfer.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_transfer.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_transfer.py index 89246cc51..45c161214 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_transfer.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_transfer.py @@ -107,7 +107,7 @@ def test_active_but_transfer_past_effective_balance(spec, state): def test_incorrect_slot(spec, state): transfer = get_valid_transfer(spec, state, slot=state.slot + 1, signed=True) # un-activate so validator can transfer - state.validators[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH + state.validators[transfer.sender].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH yield from run_transfer_processing(spec, state, transfer, False) @@ -120,7 +120,7 @@ def test_insufficient_balance_for_fee_result_dust(spec, state): transfer = get_valid_transfer(spec, state, sender_index=sender_index, amount=0, fee=1, signed=True) # un-activate so validator can transfer - state.validators[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH + state.validators[transfer.sender].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH yield from run_transfer_processing(spec, state, transfer, False) @@ -146,7 +146,7 @@ def test_insufficient_balance_for_amount_result_dust(spec, state): transfer = get_valid_transfer(spec, state, sender_index=sender_index, amount=1, fee=0, signed=True) # un-activate so validator can transfer - state.validators[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH + state.validators[transfer.sender].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH yield from run_transfer_processing(spec, state, transfer, False) @@ -287,7 +287,7 @@ def test_no_dust_sender(spec, state): ) # un-activate so validator can transfer - state.validators[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH + state.validators[transfer.sender].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH yield from run_transfer_processing(spec, state, transfer, False) @@ -301,7 +301,7 @@ def test_no_dust_recipient(spec, state): state.balances[transfer.recipient] = 0 # un-activate so validator can transfer - state.validators[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH + state.validators[transfer.sender].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH yield from run_transfer_processing(spec, state, transfer, False) @@ -313,6 +313,6 @@ def test_invalid_pubkey(spec, state): state.validators[transfer.sender].withdrawal_credentials = spec.ZERO_HASH # un-activate so validator can transfer - state.validators[transfer.sender].activation_epoch = spec.FAR_FUTURE_EPOCH + state.validators[transfer.sender].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH yield from run_transfer_processing(spec, state, transfer, False) From e79b47e3c3a24d54f341d3defb691707c0acfb73 Mon Sep 17 00:00:00 2001 From: protolambda Date: Sat, 22 Jun 2019 03:54:07 +0200 Subject: [PATCH 10/47] non-existent transfer participants tests --- .../pyspec/eth2spec/test/helpers/transfers.py | 6 ++-- .../block_processing/test_process_transfer.py | 34 +++++++++++++++---- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/helpers/transfers.py b/test_libs/pyspec/eth2spec/test/helpers/transfers.py index acc6a35c5..fa01a3088 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/transfers.py +++ b/test_libs/pyspec/eth2spec/test/helpers/transfers.py @@ -4,13 +4,15 @@ from eth2spec.utils.bls import bls_sign from eth2spec.utils.ssz.ssz_impl import signing_root -def get_valid_transfer(spec, state, slot=None, sender_index=None, amount=None, fee=None, signed=False): +def get_valid_transfer(spec, state, slot=None, sender_index=None, + recipient_index=None, amount=None, fee=None, signed=False): if slot is None: slot = state.slot current_epoch = spec.get_current_epoch(state) if sender_index is None: sender_index = spec.get_active_validator_indices(state, current_epoch)[-1] - recipient_index = spec.get_active_validator_indices(state, current_epoch)[0] + if recipient_index is None: + recipient_index = spec.get_active_validator_indices(state, current_epoch)[0] transfer_pubkey = pubkeys[-1] transfer_privkey = privkeys[-1] diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_transfer.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_transfer.py index 45c161214..ad7e6ff60 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_transfer.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_transfer.py @@ -1,7 +1,7 @@ from eth2spec.test.context import spec_state_test, expect_assertion_error, always_bls, with_all_phases from eth2spec.test.helpers.state import next_epoch from eth2spec.test.helpers.block import apply_empty_block -from eth2spec.test.helpers.transfers import get_valid_transfer +from eth2spec.test.helpers.transfers import get_valid_transfer, sign_transfer def run_transfer_processing(spec, state, transfer, valid=True): @@ -13,11 +13,6 @@ def run_transfer_processing(spec, state, transfer, valid=True): If ``valid == False``, run expecting ``AssertionError`` """ - proposer_index = spec.get_beacon_proposer_index(state) - pre_transfer_sender_balance = state.balances[transfer.sender] - pre_transfer_recipient_balance = state.balances[transfer.recipient] - pre_transfer_proposer_balance = state.balances[proposer_index] - yield 'pre', state yield 'transfer', transfer @@ -26,6 +21,11 @@ def run_transfer_processing(spec, state, transfer, valid=True): yield 'post', None return + proposer_index = spec.get_beacon_proposer_index(state) + pre_transfer_sender_balance = state.balances[transfer.sender] + pre_transfer_recipient_balance = state.balances[transfer.recipient] + pre_transfer_proposer_balance = state.balances[proposer_index] + spec.process_transfer(state, transfer) yield 'post', state @@ -306,6 +306,28 @@ def test_no_dust_recipient(spec, state): yield from run_transfer_processing(spec, state, transfer, False) +@with_all_phases +@spec_state_test +def test_non_existent_sender(spec, state): + sender_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1] + transfer = get_valid_transfer(spec, state, sender_index=sender_index, amount=1, fee=0) + transfer.sender = len(state.validators) + sign_transfer(spec, state, transfer, 42) # mostly valid signature, but sender won't exist, use bogus key. + + yield from run_transfer_processing(spec, state, transfer, False) + + +@with_all_phases +@spec_state_test +def test_non_existent_recipient(spec, state): + sender_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1] + state.balances[sender_index] = spec.MAX_EFFECTIVE_BALANCE + 1 + transfer = get_valid_transfer(spec, state, sender_index=sender_index, + recipient_index=len(state.validators), amount=1, fee=0, signed=True) + + yield from run_transfer_processing(spec, state, transfer, False) + + @with_all_phases @spec_state_test def test_invalid_pubkey(spec, state): From 6266133572494b606e12db1326fa2ac597c215af Mon Sep 17 00:00:00 2001 From: Diederik Loerakker Date: Tue, 25 Jun 2019 03:53:40 +0200 Subject: [PATCH 11/47] rename test methods based on suggestion Co-Authored-By: Danny Ryan --- .../block_processing/test_process_attester_slashing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py index 2d8f9d31a..86b6811a2 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py @@ -156,7 +156,7 @@ def test_custody_bit_0_and_1_intersect(spec, state): @with_all_phases @spec_state_test -def test_attester_slashing_invalid_att_1(spec, state): +def test_unsorted_att_1(spec, state): attester_slashing = get_valid_attester_slashing(spec, state, signed_1=False, signed_2=True) indices = attester_slashing.attestation_1.custody_bit_0_indices @@ -169,7 +169,7 @@ def test_attester_slashing_invalid_att_1(spec, state): @with_all_phases @spec_state_test -def test_attester_slashing_invalid_att_2(spec, state): +def test_unsorted_att_2(spec, state): attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=False) indices = attester_slashing.attestation_2.custody_bit_0_indices From c4b88e68e1772bd2a88987f030113309dd4a9c4d Mon Sep 17 00:00:00 2001 From: protolambda Date: Tue, 25 Jun 2019 04:19:02 +0200 Subject: [PATCH 12/47] different new-deposit tests --- .../block_processing/test_process_deposit.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_deposit.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_deposit.py index 1d4b3a107..0f94bd26c 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_deposit.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_deposit.py @@ -59,10 +59,10 @@ def run_deposit_processing(spec, state, deposit, validator_index, valid=True, ef @with_all_phases @spec_state_test -def test_new_deposit(spec, state): +def test_new_deposit_under_max(spec, state): # fresh deposit = next validator index = validator appended to registry validator_index = len(state.validators) - # effective balance will be 1 EFFECTIVE_BALANCE_INCREMENT smaller because of this small decrement. + # effective balance will be 1 EFFECTIVE_BALANCE_INCREMENT smaller because of this small decrement. amount = spec.MAX_EFFECTIVE_BALANCE - 1 deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True) @@ -71,7 +71,19 @@ def test_new_deposit(spec, state): @with_all_phases @spec_state_test -def test_new_deposit_maxed_out(spec, state): +def test_new_deposit_max(spec, state): + # fresh deposit = next validator index = validator appended to registry + validator_index = len(state.validators) + # effective balance will be exactly the same as balance. + amount = spec.MAX_EFFECTIVE_BALANCE + deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True) + + yield from run_deposit_processing(spec, state, deposit, validator_index) + + +@with_all_phases +@spec_state_test +def test_new_deposit_over_max(spec, state): # fresh deposit = next validator index = validator appended to registry validator_index = len(state.validators) # just 1 over the limit, effective balance should be set MAX_EFFECTIVE_BALANCE during processing From b4b4e9571da32e6ff13cd550cb1308cfdf703919 Mon Sep 17 00:00:00 2001 From: protolambda Date: Tue, 25 Jun 2019 04:45:53 +0200 Subject: [PATCH 13/47] test activation queue --- .../test_process_registry_updates.py | 51 +++++++++++++++---- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py index d92220910..6c1319a5b 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py @@ -30,18 +30,20 @@ def run_process_registry_updates(spec, state, valid=True): yield 'post', state -@with_all_phases -@spec_state_test -def test_activation(spec, state): - index = 0 +def mock_deposit(spec, state, index): assert spec.is_active_validator(state.validators[index], spec.get_current_epoch(state)) - - # Mock a new deposit state.validators[index].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH state.validators[index].activation_epoch = spec.FAR_FUTURE_EPOCH state.validators[index].effective_balance = spec.MAX_EFFECTIVE_BALANCE assert not spec.is_active_validator(state.validators[index], spec.get_current_epoch(state)) + +@with_all_phases +@spec_state_test +def test_activation(spec, state): + index = 0 + mock_deposit(spec, state, index) + for _ in range(spec.ACTIVATION_EXIT_DELAY + 1): next_epoch(spec, state) @@ -49,10 +51,39 @@ def test_activation(spec, state): assert state.validators[index].activation_eligibility_epoch != spec.FAR_FUTURE_EPOCH assert state.validators[index].activation_epoch != spec.FAR_FUTURE_EPOCH - assert spec.is_active_validator( - state.validators[index], - spec.get_current_epoch(state), - ) + assert spec.is_active_validator(state.validators[index], spec.get_current_epoch(state)) + + +@with_all_phases +@spec_state_test +def test_activation_queue_sorting(spec, state): + mock_activations = 10 + + epoch = spec.get_current_epoch(state) + for i in range(mock_activations): + mock_deposit(spec, state, i) + state.validators[i].activation_eligibility_epoch = epoch + 1 + + # give the last priority over the others + state.validators[mock_activations - 1].activation_eligibility_epoch = epoch + + # make sure we are hitting the churn + churn_limit = spec.get_churn_limit(state) + assert mock_activations > churn_limit + + yield from run_process_registry_updates(spec, state) + + # the first got in as second + assert state.validators[0].activation_epoch != spec.FAR_FUTURE_EPOCH + # the prioritized got in as first + assert state.validators[mock_activations - 1].activation_epoch != spec.FAR_FUTURE_EPOCH + # the second last is at the end of the queue, and did not make the churn, + # hence is not assigned an activation_epoch yet. + assert state.validators[mock_activations - 2].activation_epoch == spec.FAR_FUTURE_EPOCH + # the one at churn_limit - 1 did not make it, it was out-prioritized + assert state.validators[churn_limit - 1].activation_epoch == spec.FAR_FUTURE_EPOCH + # but the the one in front of the above did + assert state.validators[churn_limit - 2].activation_epoch != spec.FAR_FUTURE_EPOCH @with_all_phases From 0e3c2cef5ca56e011ea1ec776e8eac6aa0884f3e Mon Sep 17 00:00:00 2001 From: protolambda Date: Tue, 25 Jun 2019 15:45:57 +0200 Subject: [PATCH 14/47] fix transfer tests, add 2 new tests --- .../block_processing/test_process_transfer.py | 34 +++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_transfer.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_transfer.py index ad7e6ff60..6903f0666 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_transfer.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_transfer.py @@ -114,9 +114,37 @@ def test_incorrect_slot(spec, state): @with_all_phases @spec_state_test -def test_insufficient_balance_for_fee_result_dust(spec, state): +def test_transfer_clean(spec, state): sender_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1] - state.balances[sender_index] = spec.MAX_EFFECTIVE_BALANCE + state.balances[sender_index] = spec.MIN_DEPOSIT_AMOUNT + transfer = get_valid_transfer(spec, state, sender_index=sender_index, + amount=spec.MIN_DEPOSIT_AMOUNT, fee=0, signed=True) + + # un-activate so validator can transfer + state.validators[transfer.sender].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH + + yield from run_transfer_processing(spec, state, transfer) + + +@with_all_phases +@spec_state_test +def test_transfer_clean_split_to_fee(spec, state): + sender_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1] + state.balances[sender_index] = spec.MIN_DEPOSIT_AMOUNT + transfer = get_valid_transfer(spec, state, sender_index=sender_index, + amount=spec.MIN_DEPOSIT_AMOUNT // 2, fee=spec.MIN_DEPOSIT_AMOUNT // 2, signed=True) + + # un-activate so validator can transfer + state.validators[transfer.sender].activation_eligibility_epoch = spec.FAR_FUTURE_EPOCH + + yield from run_transfer_processing(spec, state, transfer) + + +@with_all_phases +@spec_state_test +def test_insufficient_balance_for_fee(spec, state): + sender_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1] + state.balances[sender_index] = spec.MIN_DEPOSIT_AMOUNT transfer = get_valid_transfer(spec, state, sender_index=sender_index, amount=0, fee=1, signed=True) # un-activate so validator can transfer @@ -142,7 +170,7 @@ def test_insufficient_balance_for_fee_result_full(spec, state): @spec_state_test def test_insufficient_balance_for_amount_result_dust(spec, state): sender_index = spec.get_active_validator_indices(state, spec.get_current_epoch(state))[-1] - state.balances[sender_index] = spec.MAX_EFFECTIVE_BALANCE + state.balances[sender_index] = spec.MIN_DEPOSIT_AMOUNT transfer = get_valid_transfer(spec, state, sender_index=sender_index, amount=1, fee=0, signed=True) # un-activate so validator can transfer From b2034a54a07264553f7aad38b838cedd98ff64ec Mon Sep 17 00:00:00 2001 From: protolambda Date: Tue, 25 Jun 2019 22:22:17 +0200 Subject: [PATCH 15/47] generalize epoch processing testing, add final-processing tests --- .../run_epoch_process_base.py | 44 +++++++++++++++ .../test_process_crosslinks.py | 30 ++--------- .../test_process_final_updates.py | 53 +++++++++++++++++++ .../test_process_registry_updates.py | 31 ++--------- 4 files changed, 105 insertions(+), 53 deletions(-) create mode 100644 test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/run_epoch_process_base.py create mode 100644 test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_final_updates.py diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/run_epoch_process_base.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/run_epoch_process_base.py new file mode 100644 index 000000000..13e1c3f3b --- /dev/null +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/run_epoch_process_base.py @@ -0,0 +1,44 @@ +from eth2spec.test.helpers.block import build_empty_block_for_next_slot, sign_block +from eth2spec.test.helpers.state import state_transition_and_sign_block + + +process_calls = ( + 'process_justification_and_finalization' + 'process_crosslinks' + 'process_rewards_and_penalties' + 'process_registry_updates' + 'process_reveal_deadlines' + 'process_challenge_deadlines' + 'process_slashings' + 'process_final_updates' + 'after_process_final_updates' +) + + +def run_epoch_processing_to(spec, state, process_name: str): + """ + Run the epoch processing functions up to ``process_name`` (incl.), yielding: + - pre-state ('pre'), state before calling ``process_name`` + - post-state ('post'), state after calling ``process_name`` + """ + # transition state to slot before state transition + slot = state.slot + (spec.SLOTS_PER_EPOCH - state.slot % spec.SLOTS_PER_EPOCH) - 1 + block = build_empty_block_for_next_slot(spec, state) + block.slot = slot + sign_block(spec, state, block) + state_transition_and_sign_block(spec, state, block) + + # cache state before epoch transition + spec.process_slot(state) + + # process components of epoch transition before final-updates + for name in process_calls: + if name == process_name: + break + # only run when present. Later phases introduce more to the epoch-processing. + if hasattr(spec, name): + getattr(spec, name)(state) + + yield 'pre', state + getattr(spec, process_name)(state) + yield 'post', state diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_crosslinks.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_crosslinks.py index d51191efb..a38435b96 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_crosslinks.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_crosslinks.py @@ -4,41 +4,19 @@ from eth2spec.test.context import spec_state_test, with_all_phases from eth2spec.test.helpers.state import ( next_epoch, next_slot, - state_transition_and_sign_block, ) -from eth2spec.test.helpers.block import apply_empty_block, sign_block +from eth2spec.test.helpers.block import apply_empty_block from eth2spec.test.helpers.attestations import ( add_attestation_to_state, - build_empty_block_for_next_slot, fill_aggregate_attestation, get_valid_attestation, sign_attestation, ) +from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import run_epoch_processing_to -def run_process_crosslinks(spec, state, valid=True): - """ - Run ``process_crosslinks``, yielding: - - pre-state ('pre') - - post-state ('post'). - If ``valid == False``, run expecting ``AssertionError`` - """ - # transition state to slot before state transition - slot = state.slot + (spec.SLOTS_PER_EPOCH - state.slot % spec.SLOTS_PER_EPOCH) - 1 - block = build_empty_block_for_next_slot(spec, state) - block.slot = slot - sign_block(spec, state, block) - state_transition_and_sign_block(spec, state, block) - - # cache state before epoch transition - spec.process_slot(state) - - # process components of epoch transition before processing crosslinks - spec.process_justification_and_finalization(state) - - yield 'pre', state - spec.process_crosslinks(state) - yield 'post', state +def run_process_crosslinks(spec, state): + yield from run_epoch_processing_to(spec, state, 'process_crosslinks') @with_all_phases diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_final_updates.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_final_updates.py new file mode 100644 index 000000000..ca2f9eb10 --- /dev/null +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_final_updates.py @@ -0,0 +1,53 @@ +from eth2spec.test.context import spec_state_test, with_all_phases +from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import run_epoch_processing_to + + +def run_process_final_updates(spec, state): + yield from run_epoch_processing_to(spec, state, 'process_final_updates') + + +@with_all_phases +@spec_state_test +def test_eth1_vote_reset(spec, state): + # skip ahead to near the end of the voting period + state.slot = spec.SLOTS_PER_ETH1_VOTING_PERIOD - 2 + for i in range(state.slot + 1): # add a vote for each skipped slot. + state.eth1_data_votes.append( + spec.Eth1Data(deposit_root=b'\xaa' * 32, + deposit_count=state.eth1_deposit_index, + block_hash=b'\xbb' * 32)) + + yield from run_process_final_updates(spec, state) + + assert len(state.eth1_data_votes) == 0 + + +@with_all_phases +@spec_state_test +def test_effective_balance_hysteresis(spec, state): + # Set some edge cases for balances + max = spec.MAX_EFFECTIVE_BALANCE + min = spec.EJECTION_BALANCE + inc = spec.EFFECTIVE_BALANCE_INCREMENT + half_inc = inc // 2 + cases = [ + (max, max, max), # as is + (max, max - 1, max - inc), # round down, step lower + (max, max + 1, max), # round down + (max, max - inc, max - inc), # exactly 1 step lower + (max, max - inc - 1, max - (2 * inc)), # just 1 over 1 step lower + (max, max - inc + 1, max - inc), # close to 1 step lower + (min, min + (half_inc * 3), min), # bigger balance, but not high enough + (min, min + (half_inc * 3) + 1, min + inc), # bigger balance, high enough, but small step + (min, min + (half_inc * 4) - 1, min + inc), # bigger balance, high enough, close to double step + (min, min + (half_inc * 4), min + (2 * inc)), # exact two step balance increment + (min, min + (half_inc * 4) + 1, min + (2 * inc)), # over two steps, round down + ] + for i, (pre_eff, bal, _) in enumerate(cases): + state.validators[i].effective_balance = pre_eff + state.balances[i] = bal + + yield from run_process_final_updates(spec, state) + + for i, (_, _, post_eff) in enumerate(cases): + assert state.validators[i].effective_balance == post_eff diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py index 6c1319a5b..ae76e95c2 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py @@ -1,33 +1,10 @@ -from eth2spec.test.helpers.block import build_empty_block_for_next_slot, sign_block -from eth2spec.test.helpers.state import next_epoch, state_transition_and_sign_block +from eth2spec.test.helpers.state import next_epoch from eth2spec.test.context import spec_state_test, with_all_phases +from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import run_epoch_processing_to -def run_process_registry_updates(spec, state, valid=True): - """ - Run ``process_crosslinks``, yielding: - - pre-state ('pre') - - post-state ('post'). - If ``valid == False``, run expecting ``AssertionError`` - """ - # transition state to slot before state transition - slot = state.slot + (spec.SLOTS_PER_EPOCH - state.slot % spec.SLOTS_PER_EPOCH) - 1 - block = build_empty_block_for_next_slot(spec, state) - block.slot = slot - sign_block(spec, state, block) - state_transition_and_sign_block(spec, state, block) - - # cache state before epoch transition - spec.process_slot(state) - - # process components of epoch transition before registry update - spec.process_justification_and_finalization(state) - spec.process_crosslinks(state) - spec.process_rewards_and_penalties(state) - - yield 'pre', state - spec.process_registry_updates(state) - yield 'post', state +def run_process_registry_updates(spec, state): + yield from run_epoch_processing_to(spec, state, 'process_registry_updates') def mock_deposit(spec, state, index): From c4c9bd32e2af615bae95220c0c9e2492dc5ab67b Mon Sep 17 00:00:00 2001 From: protolambda Date: Tue, 25 Jun 2019 22:26:35 +0200 Subject: [PATCH 16/47] test_eth1_vote_no_reset --- .../test_process_final_updates.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_final_updates.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_final_updates.py index ca2f9eb10..0c19f8e31 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_final_updates.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_final_updates.py @@ -6,6 +6,23 @@ def run_process_final_updates(spec, state): yield from run_epoch_processing_to(spec, state, 'process_final_updates') +@with_all_phases +@spec_state_test +def test_eth1_vote_no_reset(spec, state): + assert spec.SLOTS_PER_ETH1_VOTING_PERIOD > spec.SLOTS_PER_EPOCH + # skip ahead to near the end of the epoch + state.slot = spec.SLOTS_PER_EPOCH - 2 + for i in range(state.slot + 1): # add a vote for each skipped slot. + state.eth1_data_votes.append( + spec.Eth1Data(deposit_root=b'\xaa' * 32, + deposit_count=state.eth1_deposit_index, + block_hash=b'\xbb' * 32)) + + yield from run_process_final_updates(spec, state) + + assert len(state.eth1_data_votes) == spec.SLOTS_PER_EPOCH + + @with_all_phases @spec_state_test def test_eth1_vote_reset(spec, state): From aedd281edbcacfa68ff7ec91d4f3f2b0031a61c7 Mon Sep 17 00:00:00 2001 From: protolambda Date: Tue, 25 Jun 2019 23:18:19 +0200 Subject: [PATCH 17/47] clean up epoch processing testing --- .../run_epoch_process_base.py | 51 ++++++++--------- .../test_process_final_updates.py | 55 +++++++++++++------ 2 files changed, 60 insertions(+), 46 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/run_epoch_process_base.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/run_epoch_process_base.py index 13e1c3f3b..7e0cffc79 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/run_epoch_process_base.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/run_epoch_process_base.py @@ -1,35 +1,29 @@ -from eth2spec.test.helpers.block import build_empty_block_for_next_slot, sign_block -from eth2spec.test.helpers.state import state_transition_and_sign_block + +process_calls = [ + 'process_justification_and_finalization', + 'process_crosslinks', + 'process_rewards_and_penalties', + 'process_registry_updates', + 'process_reveal_deadlines', + 'process_challenge_deadlines', + 'process_slashings', + 'process_final_updates', + 'after_process_final_updates', +] -process_calls = ( - 'process_justification_and_finalization' - 'process_crosslinks' - 'process_rewards_and_penalties' - 'process_registry_updates' - 'process_reveal_deadlines' - 'process_challenge_deadlines' - 'process_slashings' - 'process_final_updates' - 'after_process_final_updates' -) - - -def run_epoch_processing_to(spec, state, process_name: str): +def run_epoch_processing_to(spec, state, process_name: str, exclusive=False): """ - Run the epoch processing functions up to ``process_name`` (incl.), yielding: + Run the epoch processing functions up to ``process_name``. + If ``exclusive`` is True, the process itself will not be ran. + If ``exclusive`` is False (default), this function yields: - pre-state ('pre'), state before calling ``process_name`` - post-state ('post'), state after calling ``process_name`` """ - # transition state to slot before state transition - slot = state.slot + (spec.SLOTS_PER_EPOCH - state.slot % spec.SLOTS_PER_EPOCH) - 1 - block = build_empty_block_for_next_slot(spec, state) - block.slot = slot - sign_block(spec, state, block) - state_transition_and_sign_block(spec, state, block) + slot = state.slot + (spec.SLOTS_PER_EPOCH - state.slot % spec.SLOTS_PER_EPOCH) - # cache state before epoch transition - spec.process_slot(state) + # transition state to slot before epoch state transition + spec.process_slots(state, slot) # process components of epoch transition before final-updates for name in process_calls: @@ -39,6 +33,7 @@ def run_epoch_processing_to(spec, state, process_name: str): if hasattr(spec, name): getattr(spec, name)(state) - yield 'pre', state - getattr(spec, process_name)(state) - yield 'post', state + if not exclusive: + yield 'pre', state + getattr(spec, process_name)(state) + yield 'post', state diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_final_updates.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_final_updates.py index 0c19f8e31..d1af7d396 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_final_updates.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_final_updates.py @@ -10,8 +10,8 @@ def run_process_final_updates(spec, state): @spec_state_test def test_eth1_vote_no_reset(spec, state): assert spec.SLOTS_PER_ETH1_VOTING_PERIOD > spec.SLOTS_PER_EPOCH - # skip ahead to near the end of the epoch - state.slot = spec.SLOTS_PER_EPOCH - 2 + # skip ahead to the end of the epoch + state.slot = spec.SLOTS_PER_EPOCH - 1 for i in range(state.slot + 1): # add a vote for each skipped slot. state.eth1_data_votes.append( spec.Eth1Data(deposit_root=b'\xaa' * 32, @@ -26,8 +26,8 @@ def test_eth1_vote_no_reset(spec, state): @with_all_phases @spec_state_test def test_eth1_vote_reset(spec, state): - # skip ahead to near the end of the voting period - state.slot = spec.SLOTS_PER_ETH1_VOTING_PERIOD - 2 + # skip ahead to the end of the voting period + state.slot = spec.SLOTS_PER_ETH1_VOTING_PERIOD - 1 for i in range(state.slot + 1): # add a vote for each skipped slot. state.eth1_data_votes.append( spec.Eth1Data(deposit_root=b'\xaa' * 32, @@ -42,29 +42,48 @@ def test_eth1_vote_reset(spec, state): @with_all_phases @spec_state_test def test_effective_balance_hysteresis(spec, state): + # Prepare state up to the final-updates. + # Then overwrite the balances, we only want to focus to be on the hysteresis based changes. + run_epoch_processing_to(spec, state, 'process_final_updates') # Set some edge cases for balances max = spec.MAX_EFFECTIVE_BALANCE min = spec.EJECTION_BALANCE inc = spec.EFFECTIVE_BALANCE_INCREMENT half_inc = inc // 2 cases = [ - (max, max, max), # as is - (max, max - 1, max - inc), # round down, step lower - (max, max + 1, max), # round down - (max, max - inc, max - inc), # exactly 1 step lower - (max, max - inc - 1, max - (2 * inc)), # just 1 over 1 step lower - (max, max - inc + 1, max - inc), # close to 1 step lower - (min, min + (half_inc * 3), min), # bigger balance, but not high enough - (min, min + (half_inc * 3) + 1, min + inc), # bigger balance, high enough, but small step - (min, min + (half_inc * 4) - 1, min + inc), # bigger balance, high enough, close to double step - (min, min + (half_inc * 4), min + (2 * inc)), # exact two step balance increment - (min, min + (half_inc * 4) + 1, min + (2 * inc)), # over two steps, round down + (max, max, max, "as-is"), + (max, max - 1, max - inc, "round down, step lower"), + (max, max + 1, max, "round down"), + (max, max - inc, max - inc, "exactly 1 step lower"), + (max, max - inc - 1, max - (2 * inc), "just 1 over 1 step lower"), + (max, max - inc + 1, max - inc, "close to 1 step lower"), + (min, min + (half_inc * 3), min, "bigger balance, but not high enough"), + (min, min + (half_inc * 3) + 1, min + inc, "bigger balance, high enough, but small step"), + (min, min + (half_inc * 4) - 1, min + inc, "bigger balance, high enough, close to double step"), + (min, min + (half_inc * 4), min + (2 * inc), "exact two step balance increment"), + (min, min + (half_inc * 4) + 1, min + (2 * inc), "over two steps, round down"), ] - for i, (pre_eff, bal, _) in enumerate(cases): + current_epoch = spec.get_current_epoch(state) + for i, (pre_eff, bal, _, _) in enumerate(cases): + assert spec.is_active_validator(state.validators[i], current_epoch) state.validators[i].effective_balance = pre_eff state.balances[i] = bal + yield 'pre', state + spec.process_final_updates(state) + yield 'post', state + + for i, (_, _, post_eff, name) in enumerate(cases): + assert state.validators[i].effective_balance == post_eff, name + + +@with_all_phases +@spec_state_test +def test_historical_root_accumulator(spec, state): + # skip ahead to near the end of the historical roots period (excl block before epoch processing) + state.slot = spec.SLOTS_PER_HISTORICAL_ROOT - 1 + history_len = len(state.historical_roots) + yield from run_process_final_updates(spec, state) - for i, (_, _, post_eff) in enumerate(cases): - assert state.validators[i].effective_balance == post_eff + assert len(state.historical_roots) == history_len + 1 From c66031f55cf4b24b832f917778730c0aaf8e99dd Mon Sep 17 00:00:00 2001 From: protolambda Date: Wed, 26 Jun 2019 00:01:21 +0200 Subject: [PATCH 18/47] fix crosslink tests, fix generalization of epoch processing --- .../epoch_processing/run_epoch_process_base.py | 5 ++++- .../epoch_processing/test_process_crosslinks.py | 14 ++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/run_epoch_process_base.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/run_epoch_process_base.py index 7e0cffc79..c69160fb0 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/run_epoch_process_base.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/run_epoch_process_base.py @@ -23,7 +23,10 @@ def run_epoch_processing_to(spec, state, process_name: str, exclusive=False): slot = state.slot + (spec.SLOTS_PER_EPOCH - state.slot % spec.SLOTS_PER_EPOCH) # transition state to slot before epoch state transition - spec.process_slots(state, slot) + spec.process_slots(state, slot - 1) + + # start transitioning, do one slot update before the epoch itself. + spec.process_slot(state) # process components of epoch transition before final-updates for name in process_calls: diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_crosslinks.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_crosslinks.py index a38435b96..7e93675e8 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_crosslinks.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_crosslinks.py @@ -4,8 +4,8 @@ from eth2spec.test.context import spec_state_test, with_all_phases from eth2spec.test.helpers.state import ( next_epoch, next_slot, -) -from eth2spec.test.helpers.block import apply_empty_block + state_transition_and_sign_block) +from eth2spec.test.helpers.block import apply_empty_block, build_empty_block_for_next_slot, sign_block from eth2spec.test.helpers.attestations import ( add_attestation_to_state, fill_aggregate_attestation, @@ -28,6 +28,14 @@ def test_no_attestations(spec, state): assert state.previous_crosslinks[shard] == state.current_crosslinks[shard] +def add_block_to_end_of_epoch(spec, state): + slot = state.slot + (spec.SLOTS_PER_EPOCH - state.slot % spec.SLOTS_PER_EPOCH) - 1 + block = build_empty_block_for_next_slot(spec, state) + block.slot = slot + sign_block(spec, state, block) + state_transition_and_sign_block(spec, state, block) + + @with_all_phases @spec_state_test def test_single_crosslink_update_from_current_epoch(spec, state): @@ -43,6 +51,7 @@ def test_single_crosslink_update_from_current_epoch(spec, state): shard = attestation.data.crosslink.shard pre_crosslink = deepcopy(state.current_crosslinks[shard]) + add_block_to_end_of_epoch(spec, state) yield from run_process_crosslinks(spec, state) assert state.previous_crosslinks[shard] != state.current_crosslinks[shard] @@ -66,6 +75,7 @@ def test_single_crosslink_update_from_previous_epoch(spec, state): crosslink_deltas = spec.get_crosslink_deltas(state) + add_block_to_end_of_epoch(spec, state) yield from run_process_crosslinks(spec, state) assert state.previous_crosslinks[shard] != state.current_crosslinks[shard] From 46dc3f39bb4c7d01c4e059c69de437983ac20262 Mon Sep 17 00:00:00 2001 From: protolambda Date: Wed, 26 Jun 2019 00:22:24 +0200 Subject: [PATCH 19/47] detach crosslink tests from extra block --- .../epoch_processing/test_process_crosslinks.py | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_crosslinks.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_crosslinks.py index 7e93675e8..058c93733 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_crosslinks.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_crosslinks.py @@ -3,9 +3,9 @@ from copy import deepcopy from eth2spec.test.context import spec_state_test, with_all_phases from eth2spec.test.helpers.state import ( next_epoch, - next_slot, - state_transition_and_sign_block) -from eth2spec.test.helpers.block import apply_empty_block, build_empty_block_for_next_slot, sign_block + next_slot +) +from eth2spec.test.helpers.block import apply_empty_block from eth2spec.test.helpers.attestations import ( add_attestation_to_state, fill_aggregate_attestation, @@ -28,14 +28,6 @@ def test_no_attestations(spec, state): assert state.previous_crosslinks[shard] == state.current_crosslinks[shard] -def add_block_to_end_of_epoch(spec, state): - slot = state.slot + (spec.SLOTS_PER_EPOCH - state.slot % spec.SLOTS_PER_EPOCH) - 1 - block = build_empty_block_for_next_slot(spec, state) - block.slot = slot - sign_block(spec, state, block) - state_transition_and_sign_block(spec, state, block) - - @with_all_phases @spec_state_test def test_single_crosslink_update_from_current_epoch(spec, state): @@ -51,7 +43,6 @@ def test_single_crosslink_update_from_current_epoch(spec, state): shard = attestation.data.crosslink.shard pre_crosslink = deepcopy(state.current_crosslinks[shard]) - add_block_to_end_of_epoch(spec, state) yield from run_process_crosslinks(spec, state) assert state.previous_crosslinks[shard] != state.current_crosslinks[shard] @@ -75,7 +66,6 @@ def test_single_crosslink_update_from_previous_epoch(spec, state): crosslink_deltas = spec.get_crosslink_deltas(state) - add_block_to_end_of_epoch(spec, state) yield from run_process_crosslinks(spec, state) assert state.previous_crosslinks[shard] != state.current_crosslinks[shard] From 24aa0646c095cb2ef6060345a2813991be9e94cc Mon Sep 17 00:00:00 2001 From: protolambda Date: Wed, 26 Jun 2019 03:17:46 +0200 Subject: [PATCH 20/47] new process-slashings tests, and epoch processing bugfix with transition-to-excl not working when not yielded from --- .../run_epoch_process_base.py | 23 ++-- .../test_process_crosslinks.py | 4 +- .../test_process_final_updates.py | 6 +- .../test_process_registry_updates.py | 4 +- .../test_process_slashings.py | 121 ++++++++++++++++++ 5 files changed, 142 insertions(+), 16 deletions(-) create mode 100644 test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_slashings.py diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/run_epoch_process_base.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/run_epoch_process_base.py index c69160fb0..5b2a2ece4 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/run_epoch_process_base.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/run_epoch_process_base.py @@ -12,13 +12,9 @@ process_calls = [ ] -def run_epoch_processing_to(spec, state, process_name: str, exclusive=False): +def run_epoch_processing_to(spec, state, process_name: str): """ - Run the epoch processing functions up to ``process_name``. - If ``exclusive`` is True, the process itself will not be ran. - If ``exclusive`` is False (default), this function yields: - - pre-state ('pre'), state before calling ``process_name`` - - post-state ('post'), state after calling ``process_name`` + Processes to the next epoch transition, up to, but not including, the sub-transition named ``process_name`` """ slot = state.slot + (spec.SLOTS_PER_EPOCH - state.slot % spec.SLOTS_PER_EPOCH) @@ -36,7 +32,14 @@ def run_epoch_processing_to(spec, state, process_name: str, exclusive=False): if hasattr(spec, name): getattr(spec, name)(state) - if not exclusive: - yield 'pre', state - getattr(spec, process_name)(state) - yield 'post', state + +def run_epoch_processing_with(spec, state, process_name: str): + """ + Processes to the next epoch transition, up to and including the sub-transition named ``process_name`` + - pre-state ('pre'), state before calling ``process_name`` + - post-state ('post'), state after calling ``process_name`` + """ + run_epoch_processing_to(spec, state, process_name) + yield 'pre', state + getattr(spec, process_name)(state) + yield 'post', state diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_crosslinks.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_crosslinks.py index 058c93733..599fde8e7 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_crosslinks.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_crosslinks.py @@ -12,11 +12,11 @@ from eth2spec.test.helpers.attestations import ( get_valid_attestation, sign_attestation, ) -from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import run_epoch_processing_to +from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import run_epoch_processing_with def run_process_crosslinks(spec, state): - yield from run_epoch_processing_to(spec, state, 'process_crosslinks') + yield from run_epoch_processing_with(spec, state, 'process_crosslinks') @with_all_phases diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_final_updates.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_final_updates.py index d1af7d396..58882a44f 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_final_updates.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_final_updates.py @@ -1,9 +1,11 @@ from eth2spec.test.context import spec_state_test, with_all_phases -from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import run_epoch_processing_to +from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import ( + run_epoch_processing_with, run_epoch_processing_to +) def run_process_final_updates(spec, state): - yield from run_epoch_processing_to(spec, state, 'process_final_updates') + yield from run_epoch_processing_with(spec, state, 'process_final_updates') @with_all_phases diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py index ae76e95c2..19500d4ab 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py @@ -1,10 +1,10 @@ from eth2spec.test.helpers.state import next_epoch from eth2spec.test.context import spec_state_test, with_all_phases -from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import run_epoch_processing_to +from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import run_epoch_processing_with def run_process_registry_updates(spec, state): - yield from run_epoch_processing_to(spec, state, 'process_registry_updates') + yield from run_epoch_processing_with(spec, state, 'process_registry_updates') def mock_deposit(spec, state, index): diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_slashings.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_slashings.py new file mode 100644 index 000000000..f1a23326b --- /dev/null +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_slashings.py @@ -0,0 +1,121 @@ +from eth2spec.test.context import spec_state_test, with_all_phases +from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import ( + run_epoch_processing_with, run_epoch_processing_to +) + + +def run_process_slashings(spec, state): + yield from run_epoch_processing_with(spec, state, 'process_slashings') + + +def slash_validators(spec, state, indices, out_epochs): + total_slashed_balance = 0 + for i, out_epoch in zip(indices, out_epochs): + v = state.validators[i] + v.slashed = True + spec.initiate_validator_exit(state, i) + v.withdrawable_epoch = out_epoch + total_slashed_balance += v.effective_balance + + state.slashed_balances[ + spec.get_current_epoch(state) % spec.EPOCHS_PER_SLASHED_BALANCES_VECTOR + ] = total_slashed_balance + + +@with_all_phases +@spec_state_test +def test_max_penalties(spec, state): + slashed_count = (len(state.validators) // 3) + 1 + out_epoch = spec.get_current_epoch(state) + (spec.EPOCHS_PER_SLASHED_BALANCES_VECTOR // 2) + + slashed_indices = list(range(slashed_count)) + slash_validators(spec, state, slashed_indices, [out_epoch] * slashed_count) + + total_balance = spec.get_total_active_balance(state) + total_penalties = state.slashed_balances[spec.get_current_epoch(state) % spec.EPOCHS_PER_SLASHED_BALANCES_VECTOR] + + assert total_balance // 3 <= total_penalties + + yield from run_process_slashings(spec, state) + + for i in slashed_indices: + assert state.balances[i] == 0 + + +@with_all_phases +@spec_state_test +def test_min_penalties(spec, state): + # run_epoch_processing_to(spec, state, 'process_slashings', exclusive=True) + + # Just the bare minimum for this one validator + pre_balance = state.balances[0] = state.validators[0].effective_balance = spec.EJECTION_BALANCE + # All the other validators get the maximum. + for i in range(1, len(state.validators)): + state.validators[i].effective_balance = state.balances[i] = spec.MAX_EFFECTIVE_BALANCE + + out_epoch = spec.get_current_epoch(state) + (spec.EPOCHS_PER_SLASHED_BALANCES_VECTOR // 2) + + slash_validators(spec, state, [0], [out_epoch]) + + total_balance = spec.get_total_active_balance(state) + total_penalties = state.slashed_balances[spec.get_current_epoch(state) % spec.EPOCHS_PER_SLASHED_BALANCES_VECTOR] + + # we are testing the minimum here, i.e. get slashed (effective_balance / MIN_SLASHING_PENALTY_QUOTIENT) + assert total_penalties * 3 / total_balance < 1 / spec.MIN_SLASHING_PENALTY_QUOTIENT + + yield from run_process_slashings(spec, state) + + assert state.balances[0] == pre_balance - (pre_balance // spec.MIN_SLASHING_PENALTY_QUOTIENT) + + +@with_all_phases +@spec_state_test +def test_scaled_penalties(spec, state): + # skip to next epoch + state.slot = spec.SLOTS_PER_EPOCH + + # Also mock some previous slashings, so that we test to have the delta in the penalties computation. + for i in range(spec.EPOCHS_PER_SLASHED_BALANCES_VECTOR): + state.slashed_balances[i] = spec.MAX_EFFECTIVE_BALANCE * 3 + + # Mock the very last one (which is to be used for the delta balance computation) to be different. + # To enforce the client test runner to correctly get this one from the array, not the others. + prev_penalties = state.slashed_balances[ + (spec.get_current_epoch(state) + 1) % spec.EPOCHS_PER_SLASHED_BALANCES_VECTOR + ] = spec.MAX_EFFECTIVE_BALANCE * 2 + + slashed_count = len(state.validators) // 4 + + assert slashed_count > 10 + + # make the balances non-uniform. + # Otherwise it would just be a simple 3/4 balance slashing. Test the per-validator scaled penalties. + for i in range(10): + state.validators[i].effective_balance += spec.EFFECTIVE_BALANCE_INCREMENT * 4 + state.balances[i] += spec.EFFECTIVE_BALANCE_INCREMENT * 4 + + total_balance = spec.get_total_active_balance(state) + + out_epoch = spec.get_current_epoch(state) + (spec.EPOCHS_PER_SLASHED_BALANCES_VECTOR // 2) + + slashed_indices = list(range(slashed_count)) + + # Process up to the sub-transition, then Hi-jack and get the balances. + # We just want to test the slashings. + # But we are not interested in the other balance changes during the same epoch transition. + run_epoch_processing_to(spec, state, 'process_slashings') + pre_slash_balances = list(state.balances) + + slash_validators(spec, state, slashed_indices, [out_epoch] * slashed_count) + + yield 'pre', state + spec.process_slashings(state) + yield 'post', state + + total_penalties = state.slashed_balances[spec.get_current_epoch(state) % spec.EPOCHS_PER_SLASHED_BALANCES_VECTOR] + total_penalties -= prev_penalties + + for i in slashed_indices: + v = state.validators[i] + penalty = v.effective_balance * total_penalties * 3 // total_balance + assert state.balances[i] == pre_slash_balances[i] - penalty From 7a418ed682fc3edd4d2693c4084f0e0eb2ea5130 Mon Sep 17 00:00:00 2001 From: protolambda Date: Wed, 26 Jun 2019 23:40:56 +0200 Subject: [PATCH 21/47] test messed up indices in attester slashings --- .../test_process_attester_slashing.py | 72 ++++++++++++++++++- 1 file changed, 70 insertions(+), 2 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py index 86b6811a2..226d4a561 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py @@ -154,9 +154,73 @@ def test_custody_bit_0_and_1_intersect(spec, state): yield from run_attester_slashing_processing(spec, state, attester_slashing, False) +@always_bls @with_all_phases @spec_state_test -def test_unsorted_att_1(spec, state): +def test_att1_bad_extra_index(spec, state): + attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True) + + indices = attester_slashing.attestation_1.custody_bit_0_indices + options = list(set(range(len(state.validators))) - set(indices)) + indices.append(options[len(options) // 2]) # add random index, not previously in attestation. + attester_slashing.attestation_1.custody_bit_0_indices = sorted(indices) + # Do not sign the modified attestation (it's ok to slash if attester signed, not if they did not), + # see if the bad extra index is spotted, and slashing is aborted. + + yield from run_attester_slashing_processing(spec, state, attester_slashing, False) + + +@always_bls +@with_all_phases +@spec_state_test +def test_att1_bad_replaced_index(spec, state): + attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True) + + indices = attester_slashing.attestation_1.custody_bit_0_indices + options = list(set(range(len(state.validators))) - set(indices)) + indices[3] = options[len(options) // 2] # replace with random index, not previously in attestation. + attester_slashing.attestation_1.custody_bit_0_indices = sorted(indices) + # Do not sign the modified attestation (it's ok to slash if attester signed, not if they did not), + # see if the bad replaced index is spotted, and slashing is aborted. + + yield from run_attester_slashing_processing(spec, state, attester_slashing, False) + + +@always_bls +@with_all_phases +@spec_state_test +def test_att2_bad_extra_index(spec, state): + attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True) + + indices = attester_slashing.attestation_2.custody_bit_0_indices + options = list(set(range(len(state.validators))) - set(indices)) + indices.append(options[len(options) // 2]) # add random index, not previously in attestation. + attester_slashing.attestation_2.custody_bit_0_indices = sorted(indices) + # Do not sign the modified attestation (it's ok to slash if attester signed, not if they did not), + # see if the bad extra index is spotted, and slashing is aborted. + + yield from run_attester_slashing_processing(spec, state, attester_slashing, False) + + +@always_bls +@with_all_phases +@spec_state_test +def test_att2_bad_replaced_index(spec, state): + attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True) + + indices = attester_slashing.attestation_2.custody_bit_0_indices + options = list(set(range(len(state.validators))) - set(indices)) + indices[3] = options[len(options) // 2] # replace with random index, not previously in attestation. + attester_slashing.attestation_2.custody_bit_0_indices = sorted(indices) + # Do not sign the modified attestation (it's ok to slash if attester signed, not if they did not), + # see if the bad replaced index is spotted, and slashing is aborted. + + yield from run_attester_slashing_processing(spec, state, attester_slashing, False) + + +@with_all_phases +@spec_state_test +def test_unsorted_att_1_bit0(spec, state): attester_slashing = get_valid_attester_slashing(spec, state, signed_1=False, signed_2=True) indices = attester_slashing.attestation_1.custody_bit_0_indices @@ -169,7 +233,7 @@ def test_unsorted_att_1(spec, state): @with_all_phases @spec_state_test -def test_unsorted_att_2(spec, state): +def test_unsorted_att_2_bit0(spec, state): attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=False) indices = attester_slashing.attestation_2.custody_bit_0_indices @@ -178,3 +242,7 @@ def test_unsorted_att_2(spec, state): sign_indexed_attestation(spec, state, attester_slashing.attestation_2) yield from run_attester_slashing_processing(spec, state, attester_slashing, False) + + +# note: unsorted indices for custody bit 0 are to be introduced in phase 1 testing. + From ff2d711d51ccccff9ad60d6ef6f54c9a379d0d84 Mon Sep 17 00:00:00 2001 From: protolambda Date: Thu, 27 Jun 2019 00:35:01 +0200 Subject: [PATCH 22/47] test block application on same and on previous slot state --- .../eth2spec/test/sanity/test_blocks.py | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py index bb6a6dc06..8150b06f6 100644 --- a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py +++ b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py @@ -11,7 +11,39 @@ from eth2spec.test.helpers.proposer_slashings import get_valid_proposer_slashing from eth2spec.test.helpers.attestations import get_valid_attestation from eth2spec.test.helpers.deposits import prepare_state_and_deposit -from eth2spec.test.context import spec_state_test, with_all_phases +from eth2spec.test.context import spec_state_test, with_all_phases, expect_assertion_error + + +@with_all_phases +@spec_state_test +def test_prev_slot_block_transition(spec, state): + # Go to clean slot + spec.process_slots(state, state.slot + 1) + # Make a block for it + block = build_empty_block(spec, state, slot=state.slot, signed=True) + # Transition to next slot, above block will not be invalid on top of new state. + spec.process_slots(state, state.slot + 1) + + yield 'pre', state + expect_assertion_error(lambda: state_transition_and_sign_block(spec, state, block)) + yield 'blocks', [block] + yield 'post', None + + +@with_all_phases +@spec_state_test +def test_same_slot_block_transition(spec, state): + # Same slot on top of pre-state, but move out of slot 0 first. + spec.process_slots(state, state.slot + 1) + + block = build_empty_block(spec, state, slot=state.slot, signed=True) + + yield 'pre', state + + state_transition_and_sign_block(spec, state, block) + + yield 'blocks', [block] + yield 'post', state @with_all_phases From 8445d1d90c05a29bcc8158ca6c7e9c7754b2df2c Mon Sep 17 00:00:00 2001 From: protolambda Date: Thu, 27 Jun 2019 00:37:32 +0200 Subject: [PATCH 23/47] fix formatting for lint --- .../phase_0/block_processing/test_process_attester_slashing.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py index 226d4a561..7fcbd2da5 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attester_slashing.py @@ -245,4 +245,3 @@ def test_unsorted_att_2_bit0(spec, state): # note: unsorted indices for custody bit 0 are to be introduced in phase 1 testing. - From f7b3c87715dae19119ba6b2e6d27dcc8b54d9af8 Mon Sep 17 00:00:00 2001 From: protolambda Date: Thu, 27 Jun 2019 00:43:50 +0200 Subject: [PATCH 24/47] check invalid state root --- .../pyspec/eth2spec/test/sanity/test_blocks.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py index 8150b06f6..0a879dac6 100644 --- a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py +++ b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py @@ -66,6 +66,22 @@ def test_empty_block_transition(spec, state): assert spec.get_randao_mix(state, spec.get_current_epoch(state)) != spec.ZERO_HASH +@with_all_phases +@spec_state_test +def test_invalid_state_root(spec, state): + yield 'pre', state + + block = build_empty_block_for_next_slot(spec, state) + block.state_root = b"\xaa" * 32 + sign_block(spec, state, block) + + expect_assertion_error( + lambda: spec.state_transition(state, block, validate_state_root=True)) + + yield 'blocks', [block] + yield 'post', None + + @with_all_phases @spec_state_test def test_skipped_slots(spec, state): From 235c3d6841d5a29895ea0c9465fd447f5338b92e Mon Sep 17 00:00:00 2001 From: protolambda Date: Thu, 27 Jun 2019 00:58:25 +0200 Subject: [PATCH 25/47] re-enable test_empty_epoch_transition_not_finalizing for minimal config --- .../eth2spec/test/sanity/test_blocks.py | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py index 0a879dac6..e56baee8c 100644 --- a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py +++ b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py @@ -123,26 +123,29 @@ def test_empty_epoch_transition(spec, state): assert spec.get_block_root_at_slot(state, slot) == block.parent_root -# @with_all_phases -# @spec_state_test -# def test_empty_epoch_transition_not_finalizing(spec, state): -# # copy for later balance lookups. -# pre_state = deepcopy(state) -# yield 'pre', state +@with_all_phases +@spec_state_test +def test_empty_epoch_transition_not_finalizing(spec, state): + # Don't run for non-minimal configs, it takes very long, and the effect + # of calling finalization/justifcation is just the same as with the minimal configuration. + if spec.SLOTS_PER_EPOCH > 8: + return -# block = build_empty_block_for_next_slot(spec, state) -# block.slot += spec.SLOTS_PER_EPOCH * 5 -# sign_block(spec, state, block, proposer_index=0) + # copy for later balance lookups. + pre_balances = list(state.balances) + yield 'pre', state -# state_transition_and_sign_block(spec, state, block) + spec.process_slots(state, state.slot + (spec.SLOTS_PER_EPOCH * 5)) + block = build_empty_block_for_next_slot(spec, state, signed=True) + state_transition_and_sign_block(spec, state, block) -# yield 'blocks', [block] -# yield 'post', state + yield 'blocks', [block] + yield 'post', state -# assert state.slot == block.slot -# assert state.finalized_epoch < spec.get_current_epoch(state) - 4 -# for index in range(len(state.validators)): -# assert get_balance(state, index) < get_balance(pre_state, index) + assert state.slot == block.slot + assert state.finalized_epoch < spec.get_current_epoch(state) - 4 + for index in range(len(state.validators)): + assert state.balances[index] < pre_balances[index] @with_all_phases From e49519a53b5c1492fc39978316edafeef88769d3 Mon Sep 17 00:00:00 2001 From: protolambda Date: Thu, 27 Jun 2019 02:50:49 +0200 Subject: [PATCH 26/47] wrong end epoch test --- .../test_process_attestation.py | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py index 59e99ac0c..daac7efed 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py @@ -85,6 +85,29 @@ def test_success_since_max_epochs_per_crosslink(spec, state): yield from run_attestation_processing(spec, state, attestation) +@with_all_phases +@spec_state_test +def test_wrong_end_epoch_with_max_epochs_per_crosslink(spec, state): + for _ in range(spec.MAX_EPOCHS_PER_CROSSLINK + 2): + next_epoch(spec, state) + apply_empty_block(spec, state) + + attestation = get_valid_attestation(spec, state) + data = attestation.data + # test logic sanity check: make sure the attestation only includes MAX_EPOCHS_PER_CROSSLINK epochs + assert data.crosslink.end_epoch - data.crosslink.start_epoch == spec.MAX_EPOCHS_PER_CROSSLINK + # Now change it to be different + data.crosslink.end_epoch += 1 + + sign_attestation(spec, state, attestation) + + for _ in range(spec.MIN_ATTESTATION_INCLUSION_DELAY): + next_slot(spec, state) + apply_empty_block(spec, state) + + yield from run_attestation_processing(spec, state, attestation, False) + + @with_all_phases @always_bls @spec_state_test From 384fa8854a92666694aabd5b4c923d2968438148 Mon Sep 17 00:00:00 2001 From: protolambda Date: Fri, 28 Jun 2019 00:19:55 +0200 Subject: [PATCH 27/47] justification/finalization testing groundwork --- ..._process_justification_and_finalization.py | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py new file mode 100644 index 000000000..95f00edd4 --- /dev/null +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py @@ -0,0 +1,74 @@ +from eth2spec.test.context import spec_state_test, with_all_phases +from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import ( + run_epoch_processing_with +) + + +def run_process_just_and_fin(spec, state): + yield from run_epoch_processing_with(spec, state, 'process_justification_and_finalization') + + +def get_committee_size(spec, state, slot): + epoch = spec.slot_to_epoch(slot) + epoch_start_shard = spec.get_epoch_start_shard(state, epoch) + committees_per_slot = spec.get_epoch_committee_count(state, epoch) // spec.SLOTS_PER_EPOCH + shard = (epoch_start_shard + committees_per_slot * (slot % spec.SLOTS_PER_EPOCH)) % spec.SHARD_COUNT + committee_index = (shard + spec.SHARD_COUNT - spec.get_epoch_start_shard(state, epoch)) % spec.SHARD_COUNT + committee_count = spec.get_epoch_committee_count(state, epoch) + indices = spec.get_active_validator_indices(state, epoch) + start = (len(indices) * committee_index) // committee_count + end = (len(indices) * (committee_index + 1)) // committee_count + size = end - start + return size + + +def add_mock_attestations(spec, state, target_epoch, att_count, att_ratio): + # we must be at the end of the epoch + assert (state.slot + 1) % spec.SLOTS_PER_EPOCH == 0 + + previous_epoch = spec.get_previous_epoch(state) + current_epoch = spec.get_current_epoch(state) + + if current_epoch == target_epoch: + attestations = state.current_epoch_attestations + elif previous_epoch == target_epoch: + attestations = state.previous_epoch_attestations + else: + raise Exception(f"cannot target epoch ${target_epoch} from epoch ${current_epoch}") + + total = 0 + while total < att_count: + for i in range(spec.SLOTS_PER_EPOCH): + size = get_committee_size(spec, state, state.slot + i) + # Create a bitfield filled with the given count per attestation, + # exactly on the right-most part of the committee field. + attesting_count = int(size * att_ratio) + aggregation_bitfield = ((1 << attesting_count) - 1).to_bytes(length=((size + 7) // 8), byteorder='big') + + attestations.append(spec.PendingAttestation( + aggregation_bitfield=aggregation_bitfield, + data=spec.AttestationData( + beacon_block_root=b'\xaa' * 32, + source_epoch=0, + source_root=b'\xbb' * 32, + target_root=b'\xbb' * 32, + crosslink=spec.Crosslink() + ), + inclusion_delay=0, + )) + total += 1 + + +@with_all_phases +@spec_state_test +def test_rule_1(spec, state): + previous_epoch = spec.get_previous_epoch(state) + current_epoch = spec.get_current_epoch(state) + + # TODO + # add_mock_attestations(spec, state, ...) + # get indices attesting e.g. current_epoch_attestations + # set their balances + # yield from run_process_just_and_fin(spec, state) + # check finalization + From 3a60f64b9275f0dea931bf240eba2d1f083a22e8 Mon Sep 17 00:00:00 2001 From: protolambda Date: Sat, 29 Jun 2019 01:22:29 +0200 Subject: [PATCH 28/47] refactor finalization test helper func --- ..._process_justification_and_finalization.py | 44 ++++++++++++------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py index 95f00edd4..f0fe35e8b 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py @@ -1,3 +1,4 @@ +import math from eth2spec.test.context import spec_state_test, with_all_phases from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import ( run_epoch_processing_with @@ -8,41 +9,46 @@ def run_process_just_and_fin(spec, state): yield from run_epoch_processing_with(spec, state, 'process_justification_and_finalization') -def get_committee_size(spec, state, slot): +def get_shards_for_slot(spec, state, slot): epoch = spec.slot_to_epoch(slot) epoch_start_shard = spec.get_epoch_start_shard(state, epoch) committees_per_slot = spec.get_epoch_committee_count(state, epoch) // spec.SLOTS_PER_EPOCH shard = (epoch_start_shard + committees_per_slot * (slot % spec.SLOTS_PER_EPOCH)) % spec.SHARD_COUNT - committee_index = (shard + spec.SHARD_COUNT - spec.get_epoch_start_shard(state, epoch)) % spec.SHARD_COUNT - committee_count = spec.get_epoch_committee_count(state, epoch) - indices = spec.get_active_validator_indices(state, epoch) + return [shard + i for i in range(committees_per_slot)] + + +def get_committee_size(spec, epoch_start_shard, shard, committee_count, indices): + committee_index = (shard + spec.SHARD_COUNT - epoch_start_shard) % spec.SHARD_COUNT start = (len(indices) * committee_index) // committee_count end = (len(indices) * (committee_index + 1)) // committee_count size = end - start - return size + return size, -def add_mock_attestations(spec, state, target_epoch, att_count, att_ratio): +def add_mock_attestations(spec, state, epoch, att_count, att_ratio): # we must be at the end of the epoch assert (state.slot + 1) % spec.SLOTS_PER_EPOCH == 0 previous_epoch = spec.get_previous_epoch(state) current_epoch = spec.get_current_epoch(state) - if current_epoch == target_epoch: + if current_epoch == epoch: attestations = state.current_epoch_attestations - elif previous_epoch == target_epoch: + elif previous_epoch == epoch: attestations = state.previous_epoch_attestations else: - raise Exception(f"cannot target epoch ${target_epoch} from epoch ${current_epoch}") + raise Exception(f"cannot include attestations in epoch ${epoch} from epoch ${current_epoch}") + committee_count = spec.get_epoch_committee_count(state, epoch) + indices = spec.get_active_validator_indices(state, epoch) + epoch_start_shard = spec.get_epoch_start_shard(state, epoch) total = 0 - while total < att_count: - for i in range(spec.SLOTS_PER_EPOCH): - size = get_committee_size(spec, state, state.slot + i) + for i in range(spec.SLOTS_PER_EPOCH): + for shard in get_shards_for_slot(spec, state, state.slot + i): + size = get_committee_size(spec, epoch_start_shard, shard, committee_count, indices) # Create a bitfield filled with the given count per attestation, # exactly on the right-most part of the committee field. - attesting_count = int(size * att_ratio) + attesting_count = math.ceil(size * att_ratio) aggregation_bitfield = ((1 << attesting_count) - 1).to_bytes(length=((size + 7) // 8), byteorder='big') attestations.append(spec.PendingAttestation( @@ -54,16 +60,20 @@ def add_mock_attestations(spec, state, target_epoch, att_count, att_ratio): target_root=b'\xbb' * 32, crosslink=spec.Crosslink() ), - inclusion_delay=0, + inclusion_delay=1, )) total += 1 + if total >= att_count: + return + + raise Exception(f"could not fill state with {att_count} attestations for epoch {epoch}") @with_all_phases @spec_state_test def test_rule_1(spec, state): - previous_epoch = spec.get_previous_epoch(state) - current_epoch = spec.get_current_epoch(state) + # previous_epoch = spec.get_previous_epoch(state) + # current_epoch = spec.get_current_epoch(state) # TODO # add_mock_attestations(spec, state, ...) @@ -71,4 +81,4 @@ def test_rule_1(spec, state): # set their balances # yield from run_process_just_and_fin(spec, state) # check finalization - + pass From 518db42de75b9714347cd4ad2d549691f28afe6b Mon Sep 17 00:00:00 2001 From: protolambda Date: Sat, 29 Jun 2019 03:19:30 +0200 Subject: [PATCH 29/47] fix attestation tests to work with checkpoints --- .../test/phase_0/block_processing/test_process_attestation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py index a1f87f059..f083a07f4 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/block_processing/test_process_attestation.py @@ -205,7 +205,7 @@ def test_future_target_epoch(spec, state): state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY - attestation.data.target_epoch = spec.get_current_epoch(state) + 1 # target epoch will be too new to handle + attestation.data.target.epoch = spec.get_current_epoch(state) + 1 # target epoch will be too new to handle sign_attestation(spec, state, attestation) yield from run_attestation_processing(spec, state, attestation, False) From efd9d173d786c87ad4daf4710615cd739ee2d584 Mon Sep 17 00:00:00 2001 From: protolambda Date: Sat, 29 Jun 2019 03:19:57 +0200 Subject: [PATCH 30/47] update attester slashings processing tests --- .../test_process_slashings.py | 58 ++++++++++--------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_slashings.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_slashings.py index f1a23326b..7be23a04d 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_slashings.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_slashings.py @@ -17,8 +17,8 @@ def slash_validators(spec, state, indices, out_epochs): v.withdrawable_epoch = out_epoch total_slashed_balance += v.effective_balance - state.slashed_balances[ - spec.get_current_epoch(state) % spec.EPOCHS_PER_SLASHED_BALANCES_VECTOR + state.slashings[ + spec.get_current_epoch(state) % spec.EPOCHS_PER_SLASHINGS_VECTOR ] = total_slashed_balance @@ -26,13 +26,13 @@ def slash_validators(spec, state, indices, out_epochs): @spec_state_test def test_max_penalties(spec, state): slashed_count = (len(state.validators) // 3) + 1 - out_epoch = spec.get_current_epoch(state) + (spec.EPOCHS_PER_SLASHED_BALANCES_VECTOR // 2) + out_epoch = spec.get_current_epoch(state) + (spec.EPOCHS_PER_SLASHINGS_VECTOR // 2) slashed_indices = list(range(slashed_count)) slash_validators(spec, state, slashed_indices, [out_epoch] * slashed_count) total_balance = spec.get_total_active_balance(state) - total_penalties = state.slashed_balances[spec.get_current_epoch(state) % spec.EPOCHS_PER_SLASHED_BALANCES_VECTOR] + total_penalties = sum(state.slashings) assert total_balance // 3 <= total_penalties @@ -44,28 +44,30 @@ def test_max_penalties(spec, state): @with_all_phases @spec_state_test -def test_min_penalties(spec, state): - # run_epoch_processing_to(spec, state, 'process_slashings', exclusive=True) - +def test_small_penalty(spec, state): # Just the bare minimum for this one validator - pre_balance = state.balances[0] = state.validators[0].effective_balance = spec.EJECTION_BALANCE + state.balances[0] = state.validators[0].effective_balance = spec.EJECTION_BALANCE # All the other validators get the maximum. for i in range(1, len(state.validators)): state.validators[i].effective_balance = state.balances[i] = spec.MAX_EFFECTIVE_BALANCE - out_epoch = spec.get_current_epoch(state) + (spec.EPOCHS_PER_SLASHED_BALANCES_VECTOR // 2) + out_epoch = spec.get_current_epoch(state) + (spec.EPOCHS_PER_SLASHINGS_VECTOR // 2) slash_validators(spec, state, [0], [out_epoch]) total_balance = spec.get_total_active_balance(state) - total_penalties = state.slashed_balances[spec.get_current_epoch(state) % spec.EPOCHS_PER_SLASHED_BALANCES_VECTOR] + total_penalties = sum(state.slashings) - # we are testing the minimum here, i.e. get slashed (effective_balance / MIN_SLASHING_PENALTY_QUOTIENT) - assert total_penalties * 3 / total_balance < 1 / spec.MIN_SLASHING_PENALTY_QUOTIENT + assert total_balance // 3 > total_penalties - yield from run_process_slashings(spec, state) + run_epoch_processing_to(spec, state, 'process_slashings') + pre_slash_balances = list(state.balances) + yield 'pre', state + spec.process_slashings(state) + yield 'post', state - assert state.balances[0] == pre_balance - (pre_balance // spec.MIN_SLASHING_PENALTY_QUOTIENT) + assert state.balances[0] == pre_slash_balances[0] - (state.validators[0].effective_balance + * 3 * total_penalties // total_balance) @with_all_phases @@ -75,14 +77,13 @@ def test_scaled_penalties(spec, state): state.slot = spec.SLOTS_PER_EPOCH # Also mock some previous slashings, so that we test to have the delta in the penalties computation. - for i in range(spec.EPOCHS_PER_SLASHED_BALANCES_VECTOR): - state.slashed_balances[i] = spec.MAX_EFFECTIVE_BALANCE * 3 - - # Mock the very last one (which is to be used for the delta balance computation) to be different. - # To enforce the client test runner to correctly get this one from the array, not the others. - prev_penalties = state.slashed_balances[ - (spec.get_current_epoch(state) + 1) % spec.EPOCHS_PER_SLASHED_BALANCES_VECTOR - ] = spec.MAX_EFFECTIVE_BALANCE * 2 + base = spec.EJECTION_BALANCE + incr = spec.EFFECTIVE_BALANCE_INCREMENT + # Just add some random slashings. non-zero slashings are at least the minimal effective balance. + state.slashings[0] = base + (incr * 12) + state.slashings[4] = base + (incr * 3) + state.slashings[5] = base + (incr * 6) + state.slashings[spec.EPOCHS_PER_SLASHINGS_VECTOR - 1] = base + (incr * 7) slashed_count = len(state.validators) // 4 @@ -90,13 +91,17 @@ def test_scaled_penalties(spec, state): # make the balances non-uniform. # Otherwise it would just be a simple 3/4 balance slashing. Test the per-validator scaled penalties. + diff = spec.MAX_EFFECTIVE_BALANCE - base + increments = diff // incr for i in range(10): - state.validators[i].effective_balance += spec.EFFECTIVE_BALANCE_INCREMENT * 4 - state.balances[i] += spec.EFFECTIVE_BALANCE_INCREMENT * 4 + state.validators[i].effective_balance = base + (incr * (i % increments)) + assert state.validators[i].effective_balance <= spec.MAX_EFFECTIVE_BALANCE + # add/remove some, see if balances different than the effective balances are picked up + state.balances[i] = state.validators[i].effective_balance + i - 5 total_balance = spec.get_total_active_balance(state) - out_epoch = spec.get_current_epoch(state) + (spec.EPOCHS_PER_SLASHED_BALANCES_VECTOR // 2) + out_epoch = spec.get_current_epoch(state) + (spec.EPOCHS_PER_SLASHINGS_VECTOR // 2) slashed_indices = list(range(slashed_count)) @@ -112,8 +117,7 @@ def test_scaled_penalties(spec, state): spec.process_slashings(state) yield 'post', state - total_penalties = state.slashed_balances[spec.get_current_epoch(state) % spec.EPOCHS_PER_SLASHED_BALANCES_VECTOR] - total_penalties -= prev_penalties + total_penalties = sum(state.slashings) for i in slashed_indices: v = state.validators[i] From afb34c71e6aeaa3b088b46b685f03a9dfaba8a9b Mon Sep 17 00:00:00 2001 From: protolambda Date: Sat, 29 Jun 2019 03:22:14 +0200 Subject: [PATCH 31/47] fix broken block test for checkpoint use --- test_libs/pyspec/eth2spec/test/sanity/test_blocks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py index e56baee8c..019563978 100644 --- a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py +++ b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py @@ -143,7 +143,7 @@ def test_empty_epoch_transition_not_finalizing(spec, state): yield 'post', state assert state.slot == block.slot - assert state.finalized_epoch < spec.get_current_epoch(state) - 4 + assert state.finalized_checkpoint.epoch < spec.get_current_epoch(state) - 4 for index in range(len(state.validators)): assert state.balances[index] < pre_balances[index] From cfbdee709bd408572d10c70f2a7adb3dd72220aa Mon Sep 17 00:00:00 2001 From: protolambda Date: Sat, 29 Jun 2019 05:04:25 +0200 Subject: [PATCH 32/47] finalization testing --- ..._process_justification_and_finalization.py | 226 ++++++++++++++++-- 1 file changed, 204 insertions(+), 22 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py index f0fe35e8b..0afa4f307 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py @@ -22,10 +22,10 @@ def get_committee_size(spec, epoch_start_shard, shard, committee_count, indices) start = (len(indices) * committee_index) // committee_count end = (len(indices) * (committee_index + 1)) // committee_count size = end - start - return size, + return size -def add_mock_attestations(spec, state, epoch, att_count, att_ratio): +def add_mock_attestations(spec, state, epoch, att_ratio, source, target): # we must be at the end of the epoch assert (state.slot + 1) % spec.SLOTS_PER_EPOCH == 0 @@ -42,9 +42,9 @@ def add_mock_attestations(spec, state, epoch, att_count, att_ratio): committee_count = spec.get_epoch_committee_count(state, epoch) indices = spec.get_active_validator_indices(state, epoch) epoch_start_shard = spec.get_epoch_start_shard(state, epoch) - total = 0 - for i in range(spec.SLOTS_PER_EPOCH): - for shard in get_shards_for_slot(spec, state, state.slot + i): + epoch_start_slot = spec.get_epoch_start_slot(epoch) + for slot in range(epoch_start_slot, epoch_start_slot + spec.SLOTS_PER_EPOCH): + for shard in get_shards_for_slot(spec, state, slot): size = get_committee_size(spec, epoch_start_shard, shard, committee_count, indices) # Create a bitfield filled with the given count per attestation, # exactly on the right-most part of the committee field. @@ -55,30 +55,212 @@ def add_mock_attestations(spec, state, epoch, att_count, att_ratio): aggregation_bitfield=aggregation_bitfield, data=spec.AttestationData( beacon_block_root=b'\xaa' * 32, - source_epoch=0, - source_root=b'\xbb' * 32, - target_root=b'\xbb' * 32, + source=source, + target=source, crosslink=spec.Crosslink() ), inclusion_delay=1, )) - total += 1 - if total >= att_count: - return - raise Exception(f"could not fill state with {att_count} attestations for epoch {epoch}") + +def finalize_on_234(spec, state, epoch, support): + assert epoch > 4 + state.slot = (spec.SLOTS_PER_EPOCH * epoch) - 1 # skip ahead to just before epoch + + # 43210 -- epochs ago + # 3210x -- justification bitfield indices + # 11*0. -- justification bitfield contents, . = this epoch, * is being justified now + # checkpoints for the epochs ago: + c4 = spec.Checkpoint(epoch=epoch - 4, root=b'\xaa' * 32) + c3 = spec.Checkpoint(epoch=epoch - 3, root=b'\xaa' * 32) + c2 = spec.Checkpoint(epoch=epoch - 2, root=b'\xbb' * 32) + # c1 = spec.Checkpoint(epoch=epoch - 1, root=b'\xcc' * 32) + + old_finalized = state.finalized_checkpoint + state.previous_justified_checkpoint = c4 + state.current_justified_checkpoint = c3 + bits = state.justification_bits = spec.Bitvector[spec.JUSTIFICATION_BITS_LENGTH]() + bits[3:4] = [1, 1] # mock 3rd and 4th latest epochs as justified + # mock the 2nd latest epoch as justifiable, with 4th as source + add_mock_attestations(spec, state, + epoch=epoch - 2, + att_ratio=support, + source=c4, + target=c2) + + # process! + yield from run_process_just_and_fin(spec, state) + + if support >= (2 / 3): + assert state.previous_justified_checkpoint == c3 # changed to old current + assert state.current_justified_checkpoint == c2 # changed to 2nd latest + assert state.finalized_checkpoint == c4 # finalized old previous justified epoch + else: + assert state.previous_justified_checkpoint == c3 # changed to old current + assert state.current_justified_checkpoint == c3 # still old current + assert state.finalized_checkpoint == old_finalized # no new finalized + + +def finalize_on_23(spec, state, epoch, support): + assert epoch > 3 + state.slot = (spec.SLOTS_PER_EPOCH * epoch) - 1 # skip ahead to just before epoch + + # 43210 -- epochs ago + # 3210x -- justification bitfield indices + # 01*0. -- justification bitfield contents, . = this epoch, * is being justified now + # checkpoints for the epochs ago: + # c4 = spec.Checkpoint(epoch=epoch - 4, root=b'\xaa' * 32) + c3 = spec.Checkpoint(epoch=epoch - 3, root=b'\xaa' * 32) + c2 = spec.Checkpoint(epoch=epoch - 2, root=b'\xbb' * 32) + # c1 = spec.Checkpoint(epoch=epoch - 1, root=b'\xcc' * 32) + + old_finalized = state.finalized_checkpoint + state.previous_justified_checkpoint = c3 + state.current_justified_checkpoint = c3 + bits = state.justification_bits = spec.Bitvector[spec.JUSTIFICATION_BITS_LENGTH]() + bits[2] = 1 # mock 3rd latest epoch as justified + # mock the 2nd latest epoch as justifiable, with 3rd as source + add_mock_attestations(spec, state, + epoch=epoch - 2, + att_ratio=support, + source=c3, + target=c2) + + # process! + yield from run_process_just_and_fin(spec, state) + + if support >= (2 / 3): + assert state.previous_justified_checkpoint == c3 # changed to old current + assert state.current_justified_checkpoint == c2 # changed to 2nd latest + assert state.finalized_checkpoint == c3 # finalized old previous justified epoch + else: + assert state.previous_justified_checkpoint == c3 # changed to old current + assert state.current_justified_checkpoint == c3 # still old current + assert state.finalized_checkpoint == old_finalized # no new finalized + + +def finalize_on_123(spec, state, epoch, support): + assert epoch > 3 + state.slot = (spec.SLOTS_PER_EPOCH * epoch) - 1 # skip ahead to just before epoch + + # 43210 -- epochs ago + # 3210x -- justification bitfield indices + # 011*. -- justification bitfield contents, . = this epoch, * is being justified now + # checkpoints for the epochs ago: + # c4 = spec.Checkpoint(epoch=epoch - 4, root=b'\xaa' * 32) + c3 = spec.Checkpoint(epoch=epoch - 3, root=b'\xaa' * 32) + c2 = spec.Checkpoint(epoch=epoch - 2, root=b'\xbb' * 32) + c1 = spec.Checkpoint(epoch=epoch - 1, root=b'\xcc' * 32) + + old_finalized = state.finalized_checkpoint + state.previous_justified_checkpoint = c3 + state.current_justified_checkpoint = c2 + bits = state.justification_bits = spec.Bitvector[spec.JUSTIFICATION_BITS_LENGTH]() + bits[1:2] = [1, 1] # mock 2rd and 3th latest epochs as justified + # mock the 1st latest epoch as justifiable, with 3rd as source + add_mock_attestations(spec, state, + epoch=epoch - 1, + att_ratio=support, + source=c3, + target=c1) + + # process! + yield from run_process_just_and_fin(spec, state) + + if support >= (2 / 3): + assert state.previous_justified_checkpoint == c2 # changed to old current + assert state.current_justified_checkpoint == c1 # changed to 1st latest + assert state.finalized_checkpoint == c2 # finalized old current + else: + assert state.previous_justified_checkpoint == c2 # changed to old current + assert state.current_justified_checkpoint == c2 # still old current + assert state.finalized_checkpoint == old_finalized # no new finalized + + +def finalize_on_12(spec, state, epoch, support): + assert epoch > 2 + state.slot = (spec.SLOTS_PER_EPOCH * epoch) - 1 # skip ahead to just before epoch + + # 43210 -- epochs ago + # 3210 -- justification bitfield indices + # 001*. -- justification bitfield contents, . = this epoch, * is being justified now + # checkpoints for the epochs ago: + # c4 = spec.Checkpoint(epoch=epoch - 4, root=b'\xaa' * 32) + # c3 = spec.Checkpoint(epoch=epoch - 3, root=b'\xaa' * 32) + c2 = spec.Checkpoint(epoch=epoch - 2, root=b'\xbb' * 32) + c1 = spec.Checkpoint(epoch=epoch - 1, root=b'\xcc' * 32) + + old_finalized = state.finalized_checkpoint + state.previous_justified_checkpoint = c2 + state.current_justified_checkpoint = c2 + bits = state.justification_bits = spec.Bitvector[spec.JUSTIFICATION_BITS_LENGTH]() + bits[3] = 1 # mock 3rd latest epoch as justified + # mock the 1st latest epoch as justifiable, with 2nd as source + add_mock_attestations(spec, state, + epoch=epoch - 1, + att_ratio=support, + source=c2, + target=c1) + + # process! + yield from run_process_just_and_fin(spec, state) + + if support >= (2 / 3): + assert state.previous_justified_checkpoint == c2 # changed to old current + assert state.current_justified_checkpoint == c1 # changed to 1st latest + assert state.finalized_checkpoint == c2 # finalized previous justified epoch + else: + assert state.previous_justified_checkpoint == c2 # changed to old current + assert state.current_justified_checkpoint == c2 # still old current + assert state.finalized_checkpoint == old_finalized # no new finalized @with_all_phases @spec_state_test -def test_rule_1(spec, state): - # previous_epoch = spec.get_previous_epoch(state) - # current_epoch = spec.get_current_epoch(state) +def test_234_ok_support(spec, state): + yield from finalize_on_234(spec, state, 5, 1.0) - # TODO - # add_mock_attestations(spec, state, ...) - # get indices attesting e.g. current_epoch_attestations - # set their balances - # yield from run_process_just_and_fin(spec, state) - # check finalization - pass + +@with_all_phases +@spec_state_test +def test_234_poor_support(spec, state): + yield from finalize_on_234(spec, state, 5, 0.6) + + +@with_all_phases +@spec_state_test +def test_23_ok_support(spec, state): + yield from finalize_on_23(spec, state, 4, 1.0) + + +@with_all_phases +@spec_state_test +def test_23_poor_support(spec, state): + yield from finalize_on_23(spec, state, 4, 0.6) + + +@with_all_phases +@spec_state_test +def test_123_ok_support(spec, state): + yield from finalize_on_123(spec, state, 4, 1.0) + + +@with_all_phases +@spec_state_test +def test_123_poor_support(spec, state): + yield from finalize_on_123(spec, state, 4, 0.6) + + +@with_all_phases +@spec_state_test +def test_12_ok_support(spec, state): + yield from finalize_on_12(spec, state, 3, 1.0) + + +@with_all_phases +@spec_state_test +def test_12_poor_support(spec, state): + yield from finalize_on_12(spec, state, 3, 0.6) + + +# TODO: bring ratios closer to 2/3 for edge case testing. From f484b1e98c44564c33672d49da23a42ed38ccc22 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 28 Jun 2019 23:18:22 -0600 Subject: [PATCH 33/47] some fixes to finality_12 --- .../test_process_justification_and_finalization.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py index 0afa4f307..b1d94c4fb 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py @@ -189,12 +189,14 @@ def finalize_on_12(spec, state, epoch, support): # c3 = spec.Checkpoint(epoch=epoch - 3, root=b'\xaa' * 32) c2 = spec.Checkpoint(epoch=epoch - 2, root=b'\xbb' * 32) c1 = spec.Checkpoint(epoch=epoch - 1, root=b'\xcc' * 32) + state.block_roots[spec.get_epoch_start_slot(c2.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c2.root + state.block_roots[spec.get_epoch_start_slot(c1.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c1.root old_finalized = state.finalized_checkpoint state.previous_justified_checkpoint = c2 state.current_justified_checkpoint = c2 - bits = state.justification_bits = spec.Bitvector[spec.JUSTIFICATION_BITS_LENGTH]() - bits[3] = 1 # mock 3rd latest epoch as justified + state.justification_bits = spec.Bitvector[spec.JUSTIFICATION_BITS_LENGTH]() + state.justification_bits[0] = 1 # mock latest epoch as justified # mock the 1st latest epoch as justifiable, with 2nd as source add_mock_attestations(spec, state, epoch=epoch - 1, From 1885e285e3b57a51450910e77e19cf6d505b34c6 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 28 Jun 2019 23:28:36 -0600 Subject: [PATCH 34/47] fix source/target epochs in test_12 --- .../test_process_justification_and_finalization.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py index b1d94c4fb..afb25fdae 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py @@ -187,8 +187,8 @@ def finalize_on_12(spec, state, epoch, support): # checkpoints for the epochs ago: # c4 = spec.Checkpoint(epoch=epoch - 4, root=b'\xaa' * 32) # c3 = spec.Checkpoint(epoch=epoch - 3, root=b'\xaa' * 32) - c2 = spec.Checkpoint(epoch=epoch - 2, root=b'\xbb' * 32) - c1 = spec.Checkpoint(epoch=epoch - 1, root=b'\xcc' * 32) + c2 = spec.Checkpoint(epoch=epoch - 1, root=b'\xbb' * 32) + c1 = spec.Checkpoint(epoch=epoch, root=b'\xcc' * 32) state.block_roots[spec.get_epoch_start_slot(c2.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c2.root state.block_roots[spec.get_epoch_start_slot(c1.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c1.root From bc5e947efc01141909aaf952167d6b20e6eb9d63 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 28 Jun 2019 23:38:14 -0600 Subject: [PATCH 35/47] aggregation_bitfield - bits --- .../test_process_justification_and_finalization.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py index afb25fdae..879c982f0 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py @@ -49,10 +49,10 @@ def add_mock_attestations(spec, state, epoch, att_ratio, source, target): # Create a bitfield filled with the given count per attestation, # exactly on the right-most part of the committee field. attesting_count = math.ceil(size * att_ratio) - aggregation_bitfield = ((1 << attesting_count) - 1).to_bytes(length=((size + 7) // 8), byteorder='big') + aggregation_bits = [i < attesting_count for i in range(size)] attestations.append(spec.PendingAttestation( - aggregation_bitfield=aggregation_bitfield, + aggregation_bits=aggregation_bits, data=spec.AttestationData( beacon_block_root=b'\xaa' * 32, source=source, From 022f1e7108a2d6971c147b22a3fef2b0aacebbc9 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 28 Jun 2019 23:59:20 -0600 Subject: [PATCH 36/47] fix source/target --- .../test_process_justification_and_finalization.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py index 879c982f0..246f20c9e 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py @@ -56,7 +56,7 @@ def add_mock_attestations(spec, state, epoch, att_ratio, source, target): data=spec.AttestationData( beacon_block_root=b'\xaa' * 32, source=source, - target=source, + target=target, crosslink=spec.Crosslink() ), inclusion_delay=1, @@ -187,8 +187,8 @@ def finalize_on_12(spec, state, epoch, support): # checkpoints for the epochs ago: # c4 = spec.Checkpoint(epoch=epoch - 4, root=b'\xaa' * 32) # c3 = spec.Checkpoint(epoch=epoch - 3, root=b'\xaa' * 32) - c2 = spec.Checkpoint(epoch=epoch - 1, root=b'\xbb' * 32) - c1 = spec.Checkpoint(epoch=epoch, root=b'\xcc' * 32) + c2 = spec.Checkpoint(epoch=epoch - 2, root=b'\xbb' * 32) + c1 = spec.Checkpoint(epoch=epoch - 1, root=b'\xcc' * 32) state.block_roots[spec.get_epoch_start_slot(c2.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c2.root state.block_roots[spec.get_epoch_start_slot(c1.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c1.root From 129fd6297e1218f0655dda2bf8eb2b49a75bd387 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sat, 29 Jun 2019 00:03:06 -0600 Subject: [PATCH 37/47] add shard to mock crosslink to separate attestations from eachother --- .../test_process_justification_and_finalization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py index 246f20c9e..adcf6ea1d 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py @@ -57,7 +57,7 @@ def add_mock_attestations(spec, state, epoch, att_ratio, source, target): beacon_block_root=b'\xaa' * 32, source=source, target=target, - crosslink=spec.Crosslink() + crosslink=spec.Crosslink(shard=shard) ), inclusion_delay=1, )) From b05bebf45c1bfe31fdf9ee195283827be4f145e0 Mon Sep 17 00:00:00 2001 From: Carl Beekhuizen Date: Sat, 29 Jun 2019 12:23:03 +0200 Subject: [PATCH 38/47] Fix list slicing --- ..._process_justification_and_finalization.py | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py index adcf6ea1d..3e2f6e967 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py @@ -74,13 +74,15 @@ def finalize_on_234(spec, state, epoch, support): c4 = spec.Checkpoint(epoch=epoch - 4, root=b'\xaa' * 32) c3 = spec.Checkpoint(epoch=epoch - 3, root=b'\xaa' * 32) c2 = spec.Checkpoint(epoch=epoch - 2, root=b'\xbb' * 32) - # c1 = spec.Checkpoint(epoch=epoch - 1, root=b'\xcc' * 32) + state.block_roots[spec.get_epoch_start_slot(c4.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c4.root + state.block_roots[spec.get_epoch_start_slot(c3.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c3.root + state.block_roots[spec.get_epoch_start_slot(c2.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c2.root old_finalized = state.finalized_checkpoint state.previous_justified_checkpoint = c4 state.current_justified_checkpoint = c3 - bits = state.justification_bits = spec.Bitvector[spec.JUSTIFICATION_BITS_LENGTH]() - bits[3:4] = [1, 1] # mock 3rd and 4th latest epochs as justified + state.justification_bits = spec.Bitvector[spec.JUSTIFICATION_BITS_LENGTH]() + state.justification_bits[1:3] = [1, 1] # mock 3rd and 4th latest epochs as justified # mock the 2nd latest epoch as justifiable, with 4th as source add_mock_attestations(spec, state, epoch=epoch - 2, @@ -109,16 +111,16 @@ def finalize_on_23(spec, state, epoch, support): # 3210x -- justification bitfield indices # 01*0. -- justification bitfield contents, . = this epoch, * is being justified now # checkpoints for the epochs ago: - # c4 = spec.Checkpoint(epoch=epoch - 4, root=b'\xaa' * 32) c3 = spec.Checkpoint(epoch=epoch - 3, root=b'\xaa' * 32) c2 = spec.Checkpoint(epoch=epoch - 2, root=b'\xbb' * 32) - # c1 = spec.Checkpoint(epoch=epoch - 1, root=b'\xcc' * 32) + state.block_roots[spec.get_epoch_start_slot(c3.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c3.root + state.block_roots[spec.get_epoch_start_slot(c2.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c2.root old_finalized = state.finalized_checkpoint state.previous_justified_checkpoint = c3 state.current_justified_checkpoint = c3 - bits = state.justification_bits = spec.Bitvector[spec.JUSTIFICATION_BITS_LENGTH]() - bits[2] = 1 # mock 3rd latest epoch as justified + state.justification_bits = spec.Bitvector[spec.JUSTIFICATION_BITS_LENGTH]() + state.justification_bits[1] = 1 # mock 2nd latest epoch as justified # mock the 2nd latest epoch as justifiable, with 3rd as source add_mock_attestations(spec, state, epoch=epoch - 2, @@ -147,16 +149,18 @@ def finalize_on_123(spec, state, epoch, support): # 3210x -- justification bitfield indices # 011*. -- justification bitfield contents, . = this epoch, * is being justified now # checkpoints for the epochs ago: - # c4 = spec.Checkpoint(epoch=epoch - 4, root=b'\xaa' * 32) c3 = spec.Checkpoint(epoch=epoch - 3, root=b'\xaa' * 32) c2 = spec.Checkpoint(epoch=epoch - 2, root=b'\xbb' * 32) c1 = spec.Checkpoint(epoch=epoch - 1, root=b'\xcc' * 32) + state.block_roots[spec.get_epoch_start_slot(c3.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c3.root + state.block_roots[spec.get_epoch_start_slot(c2.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c2.root + state.block_roots[spec.get_epoch_start_slot(c1.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c1.root old_finalized = state.finalized_checkpoint state.previous_justified_checkpoint = c3 state.current_justified_checkpoint = c2 - bits = state.justification_bits = spec.Bitvector[spec.JUSTIFICATION_BITS_LENGTH]() - bits[1:2] = [1, 1] # mock 2rd and 3th latest epochs as justified + state.justification_bits = spec.Bitvector[spec.JUSTIFICATION_BITS_LENGTH]() + state.justification_bits[0:2] = [1, 1] # mock 1st and 2nd latest epochs as justified # mock the 1st latest epoch as justifiable, with 3rd as source add_mock_attestations(spec, state, epoch=epoch - 1, @@ -185,8 +189,6 @@ def finalize_on_12(spec, state, epoch, support): # 3210 -- justification bitfield indices # 001*. -- justification bitfield contents, . = this epoch, * is being justified now # checkpoints for the epochs ago: - # c4 = spec.Checkpoint(epoch=epoch - 4, root=b'\xaa' * 32) - # c3 = spec.Checkpoint(epoch=epoch - 3, root=b'\xaa' * 32) c2 = spec.Checkpoint(epoch=epoch - 2, root=b'\xbb' * 32) c1 = spec.Checkpoint(epoch=epoch - 1, root=b'\xcc' * 32) state.block_roots[spec.get_epoch_start_slot(c2.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c2.root From f9ca7c97c9bc4d994ba88ee721afcfc04336673b Mon Sep 17 00:00:00 2001 From: Carl Beekhuizen Date: Sat, 29 Jun 2019 13:01:59 +0200 Subject: [PATCH 39/47] Fix 123 finalisation --- .../test_process_justification_and_finalization.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py index 3e2f6e967..42ef73084 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py @@ -149,6 +149,7 @@ def finalize_on_123(spec, state, epoch, support): # 3210x -- justification bitfield indices # 011*. -- justification bitfield contents, . = this epoch, * is being justified now # checkpoints for the epochs ago: + c4 = spec.Checkpoint(epoch=epoch - 4, root=b'\xaa' * 32) c3 = spec.Checkpoint(epoch=epoch - 3, root=b'\xaa' * 32) c2 = spec.Checkpoint(epoch=epoch - 2, root=b'\xbb' * 32) c1 = spec.Checkpoint(epoch=epoch - 1, root=b'\xcc' * 32) @@ -157,7 +158,7 @@ def finalize_on_123(spec, state, epoch, support): state.block_roots[spec.get_epoch_start_slot(c1.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c1.root old_finalized = state.finalized_checkpoint - state.previous_justified_checkpoint = c3 + state.previous_justified_checkpoint = c4 state.current_justified_checkpoint = c2 state.justification_bits = spec.Bitvector[spec.JUSTIFICATION_BITS_LENGTH]() state.justification_bits[0:2] = [1, 1] # mock 1st and 2nd latest epochs as justified From 2eca6ef09d6d94656863907ba2fed5213788f213 Mon Sep 17 00:00:00 2001 From: Carl Beekhuizen Date: Sat, 29 Jun 2019 13:10:09 +0200 Subject: [PATCH 40/47] Corrects justification comments --- .../test_process_justification_and_finalization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py index 42ef73084..02439f664 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py @@ -82,7 +82,7 @@ def finalize_on_234(spec, state, epoch, support): state.previous_justified_checkpoint = c4 state.current_justified_checkpoint = c3 state.justification_bits = spec.Bitvector[spec.JUSTIFICATION_BITS_LENGTH]() - state.justification_bits[1:3] = [1, 1] # mock 3rd and 4th latest epochs as justified + state.justification_bits[1:3] = [1, 1] # mock 2nd and 3rd latest epochs as justified # mock the 2nd latest epoch as justifiable, with 4th as source add_mock_attestations(spec, state, epoch=epoch - 2, From 0680d8cc536f72a863d9e9ec1975a2fd5ff1729c Mon Sep 17 00:00:00 2001 From: Carl Beekhuizen Date: Sat, 29 Jun 2019 14:35:07 +0200 Subject: [PATCH 41/47] Makes justification ratios more marginal --- ..._process_justification_and_finalization.py | 56 +++++++++---------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py index 02439f664..406bdbf7f 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py @@ -25,7 +25,7 @@ def get_committee_size(spec, epoch_start_shard, shard, committee_count, indices) return size -def add_mock_attestations(spec, state, epoch, att_ratio, source, target): +def add_mock_attestations(spec, state, epoch, source, target, sufficient_support=False): # we must be at the end of the epoch assert (state.slot + 1) % spec.SLOTS_PER_EPOCH == 0 @@ -48,7 +48,8 @@ def add_mock_attestations(spec, state, epoch, att_ratio, source, target): size = get_committee_size(spec, epoch_start_shard, shard, committee_count, indices) # Create a bitfield filled with the given count per attestation, # exactly on the right-most part of the committee field. - attesting_count = math.ceil(size * att_ratio) + attesting_count = math.ceil(size * 2 /3) + attesting_count = attesting_count if sufficient_support else attesting_count - 1 aggregation_bits = [i < attesting_count for i in range(size)] attestations.append(spec.PendingAttestation( @@ -63,7 +64,7 @@ def add_mock_attestations(spec, state, epoch, att_ratio, source, target): )) -def finalize_on_234(spec, state, epoch, support): +def finalize_on_234(spec, state, epoch, sufficient_support): assert epoch > 4 state.slot = (spec.SLOTS_PER_EPOCH * epoch) - 1 # skip ahead to just before epoch @@ -86,14 +87,14 @@ def finalize_on_234(spec, state, epoch, support): # mock the 2nd latest epoch as justifiable, with 4th as source add_mock_attestations(spec, state, epoch=epoch - 2, - att_ratio=support, source=c4, - target=c2) + target=c2, + sufficient_support=sufficient_support) # process! yield from run_process_just_and_fin(spec, state) - if support >= (2 / 3): + if sufficient_support: assert state.previous_justified_checkpoint == c3 # changed to old current assert state.current_justified_checkpoint == c2 # changed to 2nd latest assert state.finalized_checkpoint == c4 # finalized old previous justified epoch @@ -103,7 +104,7 @@ def finalize_on_234(spec, state, epoch, support): assert state.finalized_checkpoint == old_finalized # no new finalized -def finalize_on_23(spec, state, epoch, support): +def finalize_on_23(spec, state, epoch, sufficient_support): assert epoch > 3 state.slot = (spec.SLOTS_PER_EPOCH * epoch) - 1 # skip ahead to just before epoch @@ -124,14 +125,14 @@ def finalize_on_23(spec, state, epoch, support): # mock the 2nd latest epoch as justifiable, with 3rd as source add_mock_attestations(spec, state, epoch=epoch - 2, - att_ratio=support, source=c3, - target=c2) + target=c2, + sufficient_support=sufficient_support) # process! yield from run_process_just_and_fin(spec, state) - if support >= (2 / 3): + if sufficient_support: assert state.previous_justified_checkpoint == c3 # changed to old current assert state.current_justified_checkpoint == c2 # changed to 2nd latest assert state.finalized_checkpoint == c3 # finalized old previous justified epoch @@ -141,7 +142,7 @@ def finalize_on_23(spec, state, epoch, support): assert state.finalized_checkpoint == old_finalized # no new finalized -def finalize_on_123(spec, state, epoch, support): +def finalize_on_123(spec, state, epoch, sufficient_support): assert epoch > 3 state.slot = (spec.SLOTS_PER_EPOCH * epoch) - 1 # skip ahead to just before epoch @@ -165,14 +166,14 @@ def finalize_on_123(spec, state, epoch, support): # mock the 1st latest epoch as justifiable, with 3rd as source add_mock_attestations(spec, state, epoch=epoch - 1, - att_ratio=support, source=c3, - target=c1) + target=c1, + sufficient_support=sufficient_support) # process! yield from run_process_just_and_fin(spec, state) - if support >= (2 / 3): + if sufficient_support: assert state.previous_justified_checkpoint == c2 # changed to old current assert state.current_justified_checkpoint == c1 # changed to 1st latest assert state.finalized_checkpoint == c2 # finalized old current @@ -182,7 +183,7 @@ def finalize_on_123(spec, state, epoch, support): assert state.finalized_checkpoint == old_finalized # no new finalized -def finalize_on_12(spec, state, epoch, support): +def finalize_on_12(spec, state, epoch, sufficient_support): assert epoch > 2 state.slot = (spec.SLOTS_PER_EPOCH * epoch) - 1 # skip ahead to just before epoch @@ -203,14 +204,14 @@ def finalize_on_12(spec, state, epoch, support): # mock the 1st latest epoch as justifiable, with 2nd as source add_mock_attestations(spec, state, epoch=epoch - 1, - att_ratio=support, source=c2, - target=c1) + target=c1, + sufficient_support=sufficient_support) # process! yield from run_process_just_and_fin(spec, state) - if support >= (2 / 3): + if sufficient_support: assert state.previous_justified_checkpoint == c2 # changed to old current assert state.current_justified_checkpoint == c1 # changed to 1st latest assert state.finalized_checkpoint == c2 # finalized previous justified epoch @@ -223,49 +224,46 @@ def finalize_on_12(spec, state, epoch, support): @with_all_phases @spec_state_test def test_234_ok_support(spec, state): - yield from finalize_on_234(spec, state, 5, 1.0) + yield from finalize_on_234(spec, state, 5, True) @with_all_phases @spec_state_test def test_234_poor_support(spec, state): - yield from finalize_on_234(spec, state, 5, 0.6) + yield from finalize_on_234(spec, state, 5, False) @with_all_phases @spec_state_test def test_23_ok_support(spec, state): - yield from finalize_on_23(spec, state, 4, 1.0) + yield from finalize_on_23(spec, state, 4, True) @with_all_phases @spec_state_test def test_23_poor_support(spec, state): - yield from finalize_on_23(spec, state, 4, 0.6) + yield from finalize_on_23(spec, state, 4, False) @with_all_phases @spec_state_test def test_123_ok_support(spec, state): - yield from finalize_on_123(spec, state, 4, 1.0) + yield from finalize_on_123(spec, state, 4, True) @with_all_phases @spec_state_test def test_123_poor_support(spec, state): - yield from finalize_on_123(spec, state, 4, 0.6) + yield from finalize_on_123(spec, state, 4, False) @with_all_phases @spec_state_test def test_12_ok_support(spec, state): - yield from finalize_on_12(spec, state, 3, 1.0) + yield from finalize_on_12(spec, state, 3, True) @with_all_phases @spec_state_test def test_12_poor_support(spec, state): - yield from finalize_on_12(spec, state, 3, 0.6) - - -# TODO: bring ratios closer to 2/3 for edge case testing. + yield from finalize_on_12(spec, state, 3, False) From 4ed7af7bacb2602d97ea67596b8b21d4b019b150 Mon Sep 17 00:00:00 2001 From: Carl Beekhuizen Date: Sat, 29 Jun 2019 15:48:39 +0200 Subject: [PATCH 42/47] mock attestation refactor --- ..._process_justification_and_finalization.py | 43 +++++++++++-------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py index 406bdbf7f..66e2095b4 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py @@ -1,4 +1,3 @@ -import math from eth2spec.test.context import spec_state_test, with_all_phases from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import ( run_epoch_processing_with @@ -39,29 +38,37 @@ def add_mock_attestations(spec, state, epoch, source, target, sufficient_support else: raise Exception(f"cannot include attestations in epoch ${epoch} from epoch ${current_epoch}") - committee_count = spec.get_epoch_committee_count(state, epoch) - indices = spec.get_active_validator_indices(state, epoch) - epoch_start_shard = spec.get_epoch_start_shard(state, epoch) + total_balance = spec.get_total_active_balance(state) + remaining_balance = total_balance * 2 // 3 + epoch_start_slot = spec.get_epoch_start_slot(epoch) for slot in range(epoch_start_slot, epoch_start_slot + spec.SLOTS_PER_EPOCH): for shard in get_shards_for_slot(spec, state, slot): - size = get_committee_size(spec, epoch_start_shard, shard, committee_count, indices) + committee = spec.get_crosslink_committee(state, spec.slot_to_epoch(slot), shard) # Create a bitfield filled with the given count per attestation, # exactly on the right-most part of the committee field. - attesting_count = math.ceil(size * 2 /3) - attesting_count = attesting_count if sufficient_support else attesting_count - 1 - aggregation_bits = [i < attesting_count for i in range(size)] - attestations.append(spec.PendingAttestation( - aggregation_bits=aggregation_bits, - data=spec.AttestationData( - beacon_block_root=b'\xaa' * 32, - source=source, - target=target, - crosslink=spec.Crosslink(shard=shard) - ), - inclusion_delay=1, - )) + aggregation_bits = [0] * len(committee) + for v in range(len(committee) * 2 // 3 + 1): + if remaining_balance > 0: + remaining_balance -= state.validators[v].effective_balance + aggregation_bits[v] = 1 + elif not sufficient_support: + aggregation_bits[v - 1] = 0 + break + else: + break + + attestations.append(spec.PendingAttestation( + aggregation_bits=aggregation_bits, + data=spec.AttestationData( + beacon_block_root=b'\xaa' * 32, + source=source, + target=target, + crosslink=spec.Crosslink(shard=shard) + ), + inclusion_delay=1, + )) def finalize_on_234(spec, state, epoch, sufficient_support): From 0c29c5125f0e890e5bdd7d9e14afd166d0b6b260 Mon Sep 17 00:00:00 2001 From: Carl Beekhuizen Date: Sat, 29 Jun 2019 17:10:12 +0200 Subject: [PATCH 43/47] Finnish refactor --- .../test_process_justification_and_finalization.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py index 66e2095b4..50a0a0cab 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py @@ -16,14 +16,6 @@ def get_shards_for_slot(spec, state, slot): return [shard + i for i in range(committees_per_slot)] -def get_committee_size(spec, epoch_start_shard, shard, committee_count, indices): - committee_index = (shard + spec.SHARD_COUNT - epoch_start_shard) % spec.SHARD_COUNT - start = (len(indices) * committee_index) // committee_count - end = (len(indices) * (committee_index + 1)) // committee_count - size = end - start - return size - - def add_mock_attestations(spec, state, epoch, source, target, sufficient_support=False): # we must be at the end of the epoch assert (state.slot + 1) % spec.SLOTS_PER_EPOCH == 0 From 5d633bfdf36c5b4a9e069e302cecd70bbd782b13 Mon Sep 17 00:00:00 2001 From: protolambda Date: Sat, 29 Jun 2019 18:14:17 +0200 Subject: [PATCH 44/47] bugfix attestation creation so that it works on mainnet with multiple committees per slot, and improve bitfield index descriptions --- ..._process_justification_and_finalization.py | 63 ++++++++++--------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py index 50a0a0cab..d043d8d97 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py @@ -36,6 +36,11 @@ def add_mock_attestations(spec, state, epoch, source, target, sufficient_support epoch_start_slot = spec.get_epoch_start_slot(epoch) for slot in range(epoch_start_slot, epoch_start_slot + spec.SLOTS_PER_EPOCH): for shard in get_shards_for_slot(spec, state, slot): + # Check if we already have had sufficient balance. (and undone if we don't want it). + # If so, do not create more attestations. (we do not have empty pending attestations normally anyway) + if remaining_balance < 0: + return + committee = spec.get_crosslink_committee(state, spec.slot_to_epoch(slot), shard) # Create a bitfield filled with the given count per attestation, # exactly on the right-most part of the committee field. @@ -45,22 +50,23 @@ def add_mock_attestations(spec, state, epoch, source, target, sufficient_support if remaining_balance > 0: remaining_balance -= state.validators[v].effective_balance aggregation_bits[v] = 1 - elif not sufficient_support: - aggregation_bits[v - 1] = 0 - break else: break - attestations.append(spec.PendingAttestation( - aggregation_bits=aggregation_bits, - data=spec.AttestationData( - beacon_block_root=b'\xaa' * 32, - source=source, - target=target, - crosslink=spec.Crosslink(shard=shard) - ), - inclusion_delay=1, - )) + # remove just one attester to make the marginal support insufficient + if not sufficient_support: + aggregation_bits[aggregation_bits.index(1)] = 0 + + attestations.append(spec.PendingAttestation( + aggregation_bits=aggregation_bits, + data=spec.AttestationData( + beacon_block_root=b'\xaa' * 32, + source=source, + target=target, + crosslink=spec.Crosslink(shard=shard) + ), + inclusion_delay=1, + )) def finalize_on_234(spec, state, epoch, sufficient_support): @@ -82,7 +88,7 @@ def finalize_on_234(spec, state, epoch, sufficient_support): state.previous_justified_checkpoint = c4 state.current_justified_checkpoint = c3 state.justification_bits = spec.Bitvector[spec.JUSTIFICATION_BITS_LENGTH]() - state.justification_bits[1:3] = [1, 1] # mock 2nd and 3rd latest epochs as justified + state.justification_bits[1:3] = [1, 1] # mock 3rd and 4th latest epochs as justified (indices are pre-shift) # mock the 2nd latest epoch as justifiable, with 4th as source add_mock_attestations(spec, state, epoch=epoch - 2, @@ -93,12 +99,11 @@ def finalize_on_234(spec, state, epoch, sufficient_support): # process! yield from run_process_just_and_fin(spec, state) + assert state.previous_justified_checkpoint == c3 # changed to old current if sufficient_support: - assert state.previous_justified_checkpoint == c3 # changed to old current assert state.current_justified_checkpoint == c2 # changed to 2nd latest assert state.finalized_checkpoint == c4 # finalized old previous justified epoch else: - assert state.previous_justified_checkpoint == c3 # changed to old current assert state.current_justified_checkpoint == c3 # still old current assert state.finalized_checkpoint == old_finalized # no new finalized @@ -108,7 +113,8 @@ def finalize_on_23(spec, state, epoch, sufficient_support): state.slot = (spec.SLOTS_PER_EPOCH * epoch) - 1 # skip ahead to just before epoch # 43210 -- epochs ago - # 3210x -- justification bitfield indices + # 210xx -- justification bitfield indices (pre shift) + # 3210x -- justification bitfield indices (post shift) # 01*0. -- justification bitfield contents, . = this epoch, * is being justified now # checkpoints for the epochs ago: c3 = spec.Checkpoint(epoch=epoch - 3, root=b'\xaa' * 32) @@ -120,7 +126,7 @@ def finalize_on_23(spec, state, epoch, sufficient_support): state.previous_justified_checkpoint = c3 state.current_justified_checkpoint = c3 state.justification_bits = spec.Bitvector[spec.JUSTIFICATION_BITS_LENGTH]() - state.justification_bits[1] = 1 # mock 2nd latest epoch as justified + state.justification_bits[1] = 1 # mock 3rd latest epoch as justified (indices are pre-shift) # mock the 2nd latest epoch as justifiable, with 3rd as source add_mock_attestations(spec, state, epoch=epoch - 2, @@ -131,12 +137,11 @@ def finalize_on_23(spec, state, epoch, sufficient_support): # process! yield from run_process_just_and_fin(spec, state) + assert state.previous_justified_checkpoint == c3 # changed to old current if sufficient_support: - assert state.previous_justified_checkpoint == c3 # changed to old current assert state.current_justified_checkpoint == c2 # changed to 2nd latest assert state.finalized_checkpoint == c3 # finalized old previous justified epoch else: - assert state.previous_justified_checkpoint == c3 # changed to old current assert state.current_justified_checkpoint == c3 # still old current assert state.finalized_checkpoint == old_finalized # no new finalized @@ -146,7 +151,8 @@ def finalize_on_123(spec, state, epoch, sufficient_support): state.slot = (spec.SLOTS_PER_EPOCH * epoch) - 1 # skip ahead to just before epoch # 43210 -- epochs ago - # 3210x -- justification bitfield indices + # 210xx -- justification bitfield indices (pre shift) + # 3210x -- justification bitfield indices (post shift) # 011*. -- justification bitfield contents, . = this epoch, * is being justified now # checkpoints for the epochs ago: c4 = spec.Checkpoint(epoch=epoch - 4, root=b'\xaa' * 32) @@ -158,10 +164,10 @@ def finalize_on_123(spec, state, epoch, sufficient_support): state.block_roots[spec.get_epoch_start_slot(c1.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c1.root old_finalized = state.finalized_checkpoint - state.previous_justified_checkpoint = c4 + state.previous_justified_checkpoint = c4 # not c3, otherwise finalize 23 would trigger. state.current_justified_checkpoint = c2 state.justification_bits = spec.Bitvector[spec.JUSTIFICATION_BITS_LENGTH]() - state.justification_bits[0:2] = [1, 1] # mock 1st and 2nd latest epochs as justified + state.justification_bits[0:2] = [1, 1] # mock 2nd and 3rd latest epochs as justified (indices are pre-shift) # mock the 1st latest epoch as justifiable, with 3rd as source add_mock_attestations(spec, state, epoch=epoch - 1, @@ -172,12 +178,11 @@ def finalize_on_123(spec, state, epoch, sufficient_support): # process! yield from run_process_just_and_fin(spec, state) + assert state.previous_justified_checkpoint == c2 # changed to old current if sufficient_support: - assert state.previous_justified_checkpoint == c2 # changed to old current assert state.current_justified_checkpoint == c1 # changed to 1st latest assert state.finalized_checkpoint == c2 # finalized old current else: - assert state.previous_justified_checkpoint == c2 # changed to old current assert state.current_justified_checkpoint == c2 # still old current assert state.finalized_checkpoint == old_finalized # no new finalized @@ -187,7 +192,8 @@ def finalize_on_12(spec, state, epoch, sufficient_support): state.slot = (spec.SLOTS_PER_EPOCH * epoch) - 1 # skip ahead to just before epoch # 43210 -- epochs ago - # 3210 -- justification bitfield indices + # 210xx -- justification bitfield indices (pre shift) + # 3210x -- justification bitfield indices (post shift) # 001*. -- justification bitfield contents, . = this epoch, * is being justified now # checkpoints for the epochs ago: c2 = spec.Checkpoint(epoch=epoch - 2, root=b'\xbb' * 32) @@ -199,7 +205,7 @@ def finalize_on_12(spec, state, epoch, sufficient_support): state.previous_justified_checkpoint = c2 state.current_justified_checkpoint = c2 state.justification_bits = spec.Bitvector[spec.JUSTIFICATION_BITS_LENGTH]() - state.justification_bits[0] = 1 # mock latest epoch as justified + state.justification_bits[0] = 1 # mock 2nd latest epoch as justified (this is pre-shift) # mock the 1st latest epoch as justifiable, with 2nd as source add_mock_attestations(spec, state, epoch=epoch - 1, @@ -210,12 +216,11 @@ def finalize_on_12(spec, state, epoch, sufficient_support): # process! yield from run_process_just_and_fin(spec, state) + assert state.previous_justified_checkpoint == c2 # changed to old current if sufficient_support: - assert state.previous_justified_checkpoint == c2 # changed to old current assert state.current_justified_checkpoint == c1 # changed to 1st latest assert state.finalized_checkpoint == c2 # finalized previous justified epoch else: - assert state.previous_justified_checkpoint == c2 # changed to old current assert state.current_justified_checkpoint == c2 # still old current assert state.finalized_checkpoint == old_finalized # no new finalized From 302b3afe2ae2dcb0287096dec8b8121dca5d248f Mon Sep 17 00:00:00 2001 From: protolambda Date: Sat, 29 Jun 2019 21:37:54 +0200 Subject: [PATCH 45/47] rename/fix roots in justification tests for consistency --- .../test_process_justification_and_finalization.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py index d043d8d97..c76692a95 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py @@ -77,8 +77,8 @@ def finalize_on_234(spec, state, epoch, sufficient_support): # 3210x -- justification bitfield indices # 11*0. -- justification bitfield contents, . = this epoch, * is being justified now # checkpoints for the epochs ago: - c4 = spec.Checkpoint(epoch=epoch - 4, root=b'\xaa' * 32) - c3 = spec.Checkpoint(epoch=epoch - 3, root=b'\xaa' * 32) + c4 = spec.Checkpoint(epoch=epoch - 4, root=b'\xdd' * 32) + c3 = spec.Checkpoint(epoch=epoch - 3, root=b'\xcc' * 32) c2 = spec.Checkpoint(epoch=epoch - 2, root=b'\xbb' * 32) state.block_roots[spec.get_epoch_start_slot(c4.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c4.root state.block_roots[spec.get_epoch_start_slot(c3.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c3.root @@ -117,7 +117,7 @@ def finalize_on_23(spec, state, epoch, sufficient_support): # 3210x -- justification bitfield indices (post shift) # 01*0. -- justification bitfield contents, . = this epoch, * is being justified now # checkpoints for the epochs ago: - c3 = spec.Checkpoint(epoch=epoch - 3, root=b'\xaa' * 32) + c3 = spec.Checkpoint(epoch=epoch - 3, root=b'\xcc' * 32) c2 = spec.Checkpoint(epoch=epoch - 2, root=b'\xbb' * 32) state.block_roots[spec.get_epoch_start_slot(c3.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c3.root state.block_roots[spec.get_epoch_start_slot(c2.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c2.root @@ -155,10 +155,10 @@ def finalize_on_123(spec, state, epoch, sufficient_support): # 3210x -- justification bitfield indices (post shift) # 011*. -- justification bitfield contents, . = this epoch, * is being justified now # checkpoints for the epochs ago: - c4 = spec.Checkpoint(epoch=epoch - 4, root=b'\xaa' * 32) - c3 = spec.Checkpoint(epoch=epoch - 3, root=b'\xaa' * 32) + c4 = spec.Checkpoint(epoch=epoch - 4, root=b'\xdd' * 32) + c3 = spec.Checkpoint(epoch=epoch - 3, root=b'\xcc' * 32) c2 = spec.Checkpoint(epoch=epoch - 2, root=b'\xbb' * 32) - c1 = spec.Checkpoint(epoch=epoch - 1, root=b'\xcc' * 32) + c1 = spec.Checkpoint(epoch=epoch - 1, root=b'\xaa' * 32) state.block_roots[spec.get_epoch_start_slot(c3.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c3.root state.block_roots[spec.get_epoch_start_slot(c2.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c2.root state.block_roots[spec.get_epoch_start_slot(c1.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c1.root @@ -197,7 +197,7 @@ def finalize_on_12(spec, state, epoch, sufficient_support): # 001*. -- justification bitfield contents, . = this epoch, * is being justified now # checkpoints for the epochs ago: c2 = spec.Checkpoint(epoch=epoch - 2, root=b'\xbb' * 32) - c1 = spec.Checkpoint(epoch=epoch - 1, root=b'\xcc' * 32) + c1 = spec.Checkpoint(epoch=epoch - 1, root=b'\xaa' * 32) state.block_roots[spec.get_epoch_start_slot(c2.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c2.root state.block_roots[spec.get_epoch_start_slot(c1.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c1.root From afb33ddc5bff4a8d2ad4b456fd33587838054ba4 Mon Sep 17 00:00:00 2001 From: protolambda Date: Sat, 29 Jun 2019 21:39:10 +0200 Subject: [PATCH 46/47] fix typo in justification wording --- test_libs/pyspec/eth2spec/test/sanity/test_blocks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py index 019563978..b2eb19244 100644 --- a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py +++ b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py @@ -127,7 +127,7 @@ def test_empty_epoch_transition(spec, state): @spec_state_test def test_empty_epoch_transition_not_finalizing(spec, state): # Don't run for non-minimal configs, it takes very long, and the effect - # of calling finalization/justifcation is just the same as with the minimal configuration. + # of calling finalization/justification is just the same as with the minimal configuration. if spec.SLOTS_PER_EPOCH > 8: return From 36dd977b85ef1c21e35f691cd071b30900709759 Mon Sep 17 00:00:00 2001 From: protolambda Date: Sat, 29 Jun 2019 22:27:05 +0200 Subject: [PATCH 47/47] fix finalize on double justification in 123 rule --- ..._process_justification_and_finalization.py | 71 ++++++++++--------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py index c76692a95..1744d388c 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_justification_and_finalization.py @@ -60,7 +60,7 @@ def add_mock_attestations(spec, state, epoch, source, target, sufficient_support attestations.append(spec.PendingAttestation( aggregation_bits=aggregation_bits, data=spec.AttestationData( - beacon_block_root=b'\xaa' * 32, + beacon_block_root=b'\xff' * 32, # irrelevant to testing source=source, target=target, crosslink=spec.Crosslink(shard=shard) @@ -69,6 +69,20 @@ def add_mock_attestations(spec, state, epoch, source, target, sufficient_support )) +def get_checkpoints(spec, epoch): + c1 = None if epoch < 1 else spec.Checkpoint(epoch=epoch - 1, root=b'\xaa' * 32) + c2 = None if epoch < 2 else spec.Checkpoint(epoch=epoch - 2, root=b'\xbb' * 32) + c3 = None if epoch < 3 else spec.Checkpoint(epoch=epoch - 3, root=b'\xcc' * 32) + c4 = None if epoch < 4 else spec.Checkpoint(epoch=epoch - 4, root=b'\xdd' * 32) + c5 = None if epoch < 5 else spec.Checkpoint(epoch=epoch - 5, root=b'\xee' * 32) + return c1, c2, c3, c4, c5 + + +def put_checkpoints_in_block_roots(spec, state, checkpoints): + for c in checkpoints: + state.block_roots[spec.get_epoch_start_slot(c.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c.root + + def finalize_on_234(spec, state, epoch, sufficient_support): assert epoch > 4 state.slot = (spec.SLOTS_PER_EPOCH * epoch) - 1 # skip ahead to just before epoch @@ -77,12 +91,8 @@ def finalize_on_234(spec, state, epoch, sufficient_support): # 3210x -- justification bitfield indices # 11*0. -- justification bitfield contents, . = this epoch, * is being justified now # checkpoints for the epochs ago: - c4 = spec.Checkpoint(epoch=epoch - 4, root=b'\xdd' * 32) - c3 = spec.Checkpoint(epoch=epoch - 3, root=b'\xcc' * 32) - c2 = spec.Checkpoint(epoch=epoch - 2, root=b'\xbb' * 32) - state.block_roots[spec.get_epoch_start_slot(c4.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c4.root - state.block_roots[spec.get_epoch_start_slot(c3.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c3.root - state.block_roots[spec.get_epoch_start_slot(c2.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c2.root + c1, c2, c3, c4, _ = get_checkpoints(spec, epoch) + put_checkpoints_in_block_roots(spec, state, [c1, c2, c3, c4]) old_finalized = state.finalized_checkpoint state.previous_justified_checkpoint = c4 @@ -117,16 +127,14 @@ def finalize_on_23(spec, state, epoch, sufficient_support): # 3210x -- justification bitfield indices (post shift) # 01*0. -- justification bitfield contents, . = this epoch, * is being justified now # checkpoints for the epochs ago: - c3 = spec.Checkpoint(epoch=epoch - 3, root=b'\xcc' * 32) - c2 = spec.Checkpoint(epoch=epoch - 2, root=b'\xbb' * 32) - state.block_roots[spec.get_epoch_start_slot(c3.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c3.root - state.block_roots[spec.get_epoch_start_slot(c2.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c2.root + c1, c2, c3, _, _ = get_checkpoints(spec, epoch) + put_checkpoints_in_block_roots(spec, state, [c1, c2, c3]) old_finalized = state.finalized_checkpoint state.previous_justified_checkpoint = c3 state.current_justified_checkpoint = c3 state.justification_bits = spec.Bitvector[spec.JUSTIFICATION_BITS_LENGTH]() - state.justification_bits[1] = 1 # mock 3rd latest epoch as justified (indices are pre-shift) + state.justification_bits[1] = 1 # mock 3rd latest epoch as justified (index is pre-shift) # mock the 2nd latest epoch as justifiable, with 3rd as source add_mock_attestations(spec, state, epoch=epoch - 2, @@ -147,7 +155,7 @@ def finalize_on_23(spec, state, epoch, sufficient_support): def finalize_on_123(spec, state, epoch, sufficient_support): - assert epoch > 3 + assert epoch > 5 state.slot = (spec.SLOTS_PER_EPOCH * epoch) - 1 # skip ahead to just before epoch # 43210 -- epochs ago @@ -155,19 +163,20 @@ def finalize_on_123(spec, state, epoch, sufficient_support): # 3210x -- justification bitfield indices (post shift) # 011*. -- justification bitfield contents, . = this epoch, * is being justified now # checkpoints for the epochs ago: - c4 = spec.Checkpoint(epoch=epoch - 4, root=b'\xdd' * 32) - c3 = spec.Checkpoint(epoch=epoch - 3, root=b'\xcc' * 32) - c2 = spec.Checkpoint(epoch=epoch - 2, root=b'\xbb' * 32) - c1 = spec.Checkpoint(epoch=epoch - 1, root=b'\xaa' * 32) - state.block_roots[spec.get_epoch_start_slot(c3.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c3.root - state.block_roots[spec.get_epoch_start_slot(c2.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c2.root - state.block_roots[spec.get_epoch_start_slot(c1.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c1.root + c1, c2, c3, c4, c5 = get_checkpoints(spec, epoch) + put_checkpoints_in_block_roots(spec, state, [c1, c2, c3, c4, c5]) old_finalized = state.finalized_checkpoint - state.previous_justified_checkpoint = c4 # not c3, otherwise finalize 23 would trigger. - state.current_justified_checkpoint = c2 + state.previous_justified_checkpoint = c5 + state.current_justified_checkpoint = c3 state.justification_bits = spec.Bitvector[spec.JUSTIFICATION_BITS_LENGTH]() - state.justification_bits[0:2] = [1, 1] # mock 2nd and 3rd latest epochs as justified (indices are pre-shift) + state.justification_bits[1] = 1 # mock 3rd latest epochs as justified (index is pre-shift) + # mock the 2nd latest epoch as justifiable, with 5th as source + add_mock_attestations(spec, state, + epoch=epoch - 2, + source=c5, + target=c2, + sufficient_support=sufficient_support) # mock the 1st latest epoch as justifiable, with 3rd as source add_mock_attestations(spec, state, epoch=epoch - 1, @@ -178,12 +187,12 @@ def finalize_on_123(spec, state, epoch, sufficient_support): # process! yield from run_process_just_and_fin(spec, state) - assert state.previous_justified_checkpoint == c2 # changed to old current + assert state.previous_justified_checkpoint == c3 # changed to old current if sufficient_support: assert state.current_justified_checkpoint == c1 # changed to 1st latest - assert state.finalized_checkpoint == c2 # finalized old current + assert state.finalized_checkpoint == c3 # finalized old current else: - assert state.current_justified_checkpoint == c2 # still old current + assert state.current_justified_checkpoint == c3 # still old current assert state.finalized_checkpoint == old_finalized # no new finalized @@ -196,10 +205,8 @@ def finalize_on_12(spec, state, epoch, sufficient_support): # 3210x -- justification bitfield indices (post shift) # 001*. -- justification bitfield contents, . = this epoch, * is being justified now # checkpoints for the epochs ago: - c2 = spec.Checkpoint(epoch=epoch - 2, root=b'\xbb' * 32) - c1 = spec.Checkpoint(epoch=epoch - 1, root=b'\xaa' * 32) - state.block_roots[spec.get_epoch_start_slot(c2.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c2.root - state.block_roots[spec.get_epoch_start_slot(c1.epoch) % spec.SLOTS_PER_HISTORICAL_ROOT] = c1.root + c1, c2, _, _, _ = get_checkpoints(spec, epoch) + put_checkpoints_in_block_roots(spec, state, [c1, c2]) old_finalized = state.finalized_checkpoint state.previous_justified_checkpoint = c2 @@ -252,13 +259,13 @@ def test_23_poor_support(spec, state): @with_all_phases @spec_state_test def test_123_ok_support(spec, state): - yield from finalize_on_123(spec, state, 4, True) + yield from finalize_on_123(spec, state, 6, True) @with_all_phases @spec_state_test def test_123_poor_support(spec, state): - yield from finalize_on_123(spec, state, 4, False) + yield from finalize_on_123(spec, state, 6, False) @with_all_phases