diff --git a/AllTests-mainnet.md b/AllTests-mainnet.md index 94c0c1bf8..50c18b484 100644 --- a/AllTests-mainnet.md +++ b/AllTests-mainnet.md @@ -521,18 +521,6 @@ OK: 7/7 Fail: 0/7 Skip: 0/7 + default initialization of signatures OK ``` OK: 3/3 Fail: 0/3 Skip: 0/3 -## [Unit - Spec - Epoch processing] Justification and Finalization [Preset: mainnet] -```diff -+ Rule I - 234 finalization with enough support OK -+ Rule I - 234 finalization without support OK -+ Rule II - 23 finalization with enough support OK -+ Rule II - 23 finalization without support OK -+ Rule III - 123 finalization with enough support OK -+ Rule III - 123 finalization without support OK -+ Rule IV - 12 finalization with enough support OK -+ Rule IV - 12 finalization without support OK -``` -OK: 8/8 Fail: 0/8 Skip: 0/8 ## chain DAG finalization tests [Preset: mainnet] ```diff + init with gaps [Preset: mainnet] OK @@ -620,4 +608,4 @@ OK: 2/2 Fail: 0/2 Skip: 0/2 OK: 9/9 Fail: 0/9 Skip: 0/9 ---TOTAL--- -OK: 345/350 Fail: 0/350 Skip: 5/350 +OK: 337/342 Fail: 0/342 Skip: 5/342 diff --git a/tests/spec_epoch_processing/eth2-finalization.png b/docs/eth2-finalization.png similarity index 100% rename from tests/spec_epoch_processing/eth2-finalization.png rename to docs/eth2-finalization.png diff --git a/tests/all_tests.nim b/tests/all_tests.nim index 365cc4a86..627740898 100644 --- a/tests/all_tests.nim +++ b/tests/all_tests.nim @@ -50,10 +50,6 @@ import # Unit test ./slashing_protection/test_fixtures, ./slashing_protection/test_slashing_protection_db -import # Refactor state transition unit tests - # In mainnet these take 2 minutes and are empty TODOs - ./spec_epoch_processing/test_process_justification_and_finalization - when not defined(i386): # Avoids "Out of memory" CI failures import diff --git a/tests/spec_epoch_processing/README.md b/tests/spec_epoch_processing/README.md deleted file mode 100644 index 18d4ba99e..000000000 --- a/tests/spec_epoch_processing/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Epoch state transition - -## Finalization and justification - -![](eth2-finalization.png) diff --git a/tests/spec_epoch_processing/epoch_utils.nim b/tests/spec_epoch_processing/epoch_utils.nim deleted file mode 100644 index c33ffaff5..000000000 --- a/tests/spec_epoch_processing/epoch_utils.nim +++ /dev/null @@ -1,42 +0,0 @@ -# beacon_chain -# Copyright (c) 2018-2021 Status Research & Development GmbH -# Licensed and distributed under either of -# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). -# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). -# at your option. This file may not be copied, modified, or distributed except according to those terms. - -import - # Specs - ../../beacon_chain/spec/[ - forks, presets, state_transition, state_transition_epoch], - ../../beacon_chain/spec/datatypes/phase0 - -proc processSlotsUntilEndCurrentEpoch(state: var ForkedHashedBeaconState) = - # Process all slots until the end of the last slot of the current epoch - var - cache = StateCache() - info = ForkedEpochInfo() - let slot = - getStateField(state, slot) + SLOTS_PER_EPOCH - - (getStateField(state, slot) mod SLOTS_PER_EPOCH) - - # Transition to slot before the epoch state transition - discard process_slots(defaultRuntimeConfig, state, slot - 1, cache, info, {}) - - # For the last slot of the epoch, - # only process_slot without process_epoch - # (see process_slots()) - state.root is invalid after here! - process_slot(state.phase0Data.data, getStateRoot(state)) - -proc transitionEpochUntilJustificationFinalization*(state: var ForkedHashedBeaconState) = - # Process slots and do the epoch transition until crosslinks - processSlotsUntilEndCurrentEpoch(state) - - var - cache = StateCache() - info: phase0.EpochInfo - - info.init(state.phase0Data.data) - info.process_attestations(state.phase0Data.data, cache) - process_justification_and_finalization( - state.phase0Data.data, info.balances) diff --git a/tests/spec_epoch_processing/justification_finalization_helpers.nim b/tests/spec_epoch_processing/justification_finalization_helpers.nim deleted file mode 100644 index b9d49f69d..000000000 --- a/tests/spec_epoch_processing/justification_finalization_helpers.nim +++ /dev/null @@ -1,90 +0,0 @@ -# beacon_chain -# Copyright (c) 2018-2022 Status Research & Development GmbH -# Licensed and distributed under either of -# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). -# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). -# at your option. This file may not be copied, modified, or distributed except according to those terms. - -import - # Standard library - std/strformat, - # Specs - ../../beacon_chain/spec/datatypes/phase0, - ../../beacon_chain/spec/[beaconstate, validator, helpers], - # Test helpers - ../helpers/digest_helpers - -# Justification and finalization utils -# --------------------------------------------------------------- - -func addMockAttestations*( - state: var phase0.BeaconState, epoch: Epoch, - source, target: Checkpoint, - sufficient_support = false - ) = - # We must be at the end of the epoch - doAssert (state.slot + 1).is_epoch - - # Alias the attestations container - var attestations: ptr seq[PendingAttestation] - if state.get_current_epoch() == epoch: - attestations = state.current_epoch_attestations.asSeq.addr - elif state.get_previous_epoch() == epoch: - attestations = state.previous_epoch_attestations.asSeq.addr - else: - raise newException(ValueError, &"Cannot include attestations from epoch {state.get_current_epoch()} in epoch {epoch}") - - # TODO: Working with an unsigned Gwei balance is a recipe for underflows to happen - var cache = StateCache() - var remaining_balance = state.get_total_active_balance(cache).int64 * 2 div 3 - - let - committees_per_slot = get_committee_count_per_slot(state, epoch, cache) - - for slot in epoch.slots(): - for committee_index in get_committee_indices(committees_per_slot): - let committee = get_beacon_committee(state, slot, committee_index, cache) - - # Create a bitfield filled with the given count per attestation, - # exactly on the right-most part of the committee field. - var aggregation_bits = init(CommitteeValidatorsBits, committee.len) - for v in 0 ..< committee.len * 2 div 3 + 1: - if remaining_balance > 0: - # Beware of the underflows, use int - remaining_balance -= state.validators.item(v).effective_balance.int64 - aggregation_bits[v] = true - else: - break - - # Remove just one attester to make the marginal support insufficient - if not sufficient_support: - # Find the first attester if any - let idx = aggregation_bits.find(true) - if idx != -1: - aggregation_bits[idx] = false - - attestations[].add PendingAttestation( - aggregation_bits: aggregation_bits, - data: AttestationData( - slot: slot.Slot, - index: committee_index.uint64, - beacon_block_root: [byte 0xFF] * 32, # Irrelevant for testing - source: source, - target: target, - ), - inclusion_delay: 1 - ) - -func getCheckpoints*(epoch: Epoch): tuple[c1, c2, c3, c4, c5: Checkpoint] = - if epoch >= 1: result.c1 = Checkpoint(epoch: epoch - 1, root: [byte 0xAA] * 32) - if epoch >= 2: result.c2 = Checkpoint(epoch: epoch - 2, root: [byte 0xBB] * 32) - if epoch >= 3: result.c3 = Checkpoint(epoch: epoch - 3, root: [byte 0xCC] * 32) - if epoch >= 4: result.c4 = Checkpoint(epoch: epoch - 4, root: [byte 0xDD] * 32) - if epoch >= 5: result.c5 = Checkpoint(epoch: epoch - 5, root: [byte 0xEE] * 32) - -func putCheckpointsInBlockRoots*( - state: var phase0.BeaconState, - checkpoints: openArray[Checkpoint]) = - for c in checkpoints: - let idx = c.epoch.start_slot() mod SLOTS_PER_HISTORICAL_ROOT - state.block_roots[idx] = c.root diff --git a/tests/spec_epoch_processing/test_process_justification_and_finalization.nim b/tests/spec_epoch_processing/test_process_justification_and_finalization.nim deleted file mode 100644 index 7f533a52a..000000000 --- a/tests/spec_epoch_processing/test_process_justification_and_finalization.nim +++ /dev/null @@ -1,251 +0,0 @@ -# beacon_chain -# Copyright (c) 2018-2022 Status Research & Development GmbH -# Licensed and distributed under either of -# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). -# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). -# at your option. This file may not be copied, modified, or distributed except according to those terms. - -{.used.} - -import - # Standard library - # Vendored packages - stew/bitops2, - # Specs - ../../beacon_chain/spec/datatypes/base, - ../../beacon_chain/spec/forks, - # Test helpers - ../mocking/mock_genesis, - ./epoch_utils, - ./justification_finalization_helpers, - ../testutil - -# See diagram: eth2-finalization.png -# (source) https://github.com/protolambda/eth2-docs#justification-and-finalization -# for a visualization of finalization rules - -proc finalizeOn234( - state: var ForkedHashedBeaconState, epoch: Epoch, sufficient_support: bool) = - ## Check finalization on rule 1 "234" - doAssert epoch > 4 - getStateField(state, slot) = Slot((epoch * SLOTS_PER_EPOCH) - 1) # Skip ahead to just before epoch - - # 43210 -- epochs ago - # 3210x -- justification bitfields indices - # 11*0. -- justification bitfield contents. . = this epoch, * is being justified now - - # checkpoints for epochs ago - let (c1, c2, c3, c4, _) = getCheckpoints(epoch) - putCheckpointsInBlockRoots(state.phase0Data.data, [c1, c2, c3, c4]) - - # Save for final checks - let old_finalized = getStateField(state, finalized_checkpoint) - - # Mock the state - getStateField(state, previous_justified_checkpoint) = c4 - getStateField(state, current_justified_checkpoint) = c3 - getStateField(state, justification_bits) = JustificationBits(0'u8) # Bitvector of length 4 - # mock 3rd and 4th latest epochs as justified - # indices are pre-shift - uint8(getStateField(state, justification_bits)).setBit 1 - uint8(getStateField(state, justification_bits)).setBit 2 - # mock the 2nd latest epoch as justifiable, with 4th as the source - addMockAttestations( - state.phase0Data.data, - epoch = epoch - 2, - source = c4, - target = c2, - sufficient_support = sufficient_support - ) - - # State transition - transitionEpochUntilJustificationFinalization(state) - - # Checks - doAssert getStateField(state, previous_justified_checkpoint) == c3 # changed to old current - if sufficient_support: - doAssert getStateField(state, current_justified_checkpoint) == c2 # changed to second latest - doAssert getStateField(state, finalized_checkpoint) == c4 # finalized old previous justified epoch - else: - doAssert getStateField(state, current_justified_checkpoint) == c3 # still old current - doAssert getStateField(state, finalized_checkpoint) == old_finalized # no new finalized checkpoint - -proc finalizeOn23(state: var ForkedHashedBeaconState, epoch: Epoch, sufficient_support: bool) = - ## Check finalization on rule 2 "23" - doAssert epoch > 3 - getStateField(state, slot) = Slot((epoch * SLOTS_PER_EPOCH) - 1) # Skip ahead to just before epoch - - # 43210 -- epochs ago - # 210xx -- justification bitfields indices preshift - # 3210x -- justification bitfield indices postshift - # 01*0. -- justification bitfield contents. . = this epoch, * is being justified now - - # checkpoints for epochs ago - let (c1, c2, c3, _, _) = getCheckpoints(epoch) - putCheckpointsInBlockRoots(state.phase0Data.data, [c1, c2, c3]) - - # Save for final checks - let old_finalized = getStateField(state, finalized_checkpoint) - - # Mock the state - getStateField(state, previous_justified_checkpoint) = c3 - getStateField(state, current_justified_checkpoint) = c3 - getStateField(state, justification_bits) = JustificationBits(0'u8) # Bitvector of length 4 - # mock 3rd as justified - # indices are pre-shift - uint8(getStateField(state, justification_bits)).setBit 1 - # mock the 2nd latest epoch as justifiable, with 3rd as the source - addMockAttestations( - state.phase0Data.data, - epoch = epoch - 2, - source = c3, - target = c2, - sufficient_support = sufficient_support - ) - - # State transition - transitionEpochUntilJustificationFinalization(state) - - # Checks - doAssert getStateField(state, previous_justified_checkpoint) == c3 # changed to old current - if sufficient_support: - doAssert getStateField(state, current_justified_checkpoint) == c2 # changed to second latest - doAssert getStateField(state, finalized_checkpoint) == c3 # finalized old previous justified epoch - else: - doAssert getStateField(state, current_justified_checkpoint) == c3 # still old current - doAssert getStateField(state, finalized_checkpoint) == old_finalized # no new finalized checkpoint - -proc finalizeOn123(state: var ForkedHashedBeaconState, epoch: Epoch, sufficient_support: bool) = - ## Check finalization on rule 3 "123" - doAssert epoch > 5 - getStateField(state, slot) = Slot((epoch * SLOTS_PER_EPOCH) - 1) # Skip ahead to just before epoch - - # 43210 -- epochs ago - # 210xx -- justification bitfields indices preshift - # 3210x -- justification bitfield indices postshift - # 0110*. -- justification bitfield contents. . = this epoch, * is being justified now - - # checkpoints for epochs ago - let (c1, c2, c3, c4, c5) = getCheckpoints(epoch) - putCheckpointsInBlockRoots(state.phase0Data.data, [c1, c2, c3, c4, c5]) - - # Save for final checks - let old_finalized = getStateField(state, finalized_checkpoint) - - # Mock the state - getStateField(state, previous_justified_checkpoint) = c5 - getStateField(state, current_justified_checkpoint) = c3 - getStateField(state, justification_bits) = JustificationBits(0'u8) # Bitvector of length 4 - # mock 3rd as justified - # indices are pre-shift - uint8(getStateField(state, justification_bits)).setBit 1 - # mock the 2nd latest epoch as justifiable, with 5th as the source - addMockAttestations( - state.phase0Data.data, - epoch = epoch - 2, - source = c5, - target = c2, - sufficient_support = sufficient_support - ) - # mock the 1st latest epoch as justifiable with 3rd as source - addMockAttestations( - state.phase0Data.data, - epoch = epoch - 1, - source = c3, - target = c1, - sufficient_support = sufficient_support - ) - - # State transition - transitionEpochUntilJustificationFinalization(state) - - # Checks - doAssert getStateField(state, previous_justified_checkpoint) == c3 # changed to old current - if sufficient_support: - doAssert getStateField(state, current_justified_checkpoint) == c1 # changed to second latest - doAssert getStateField(state, finalized_checkpoint) == c3 # finalized old previous justified epoch - else: - doAssert getStateField(state, current_justified_checkpoint) == c3 # still old current - doAssert getStateField(state, finalized_checkpoint) == old_finalized # no new finalized checkpoint - -proc finalizeOn12(state: var ForkedHashedBeaconState, epoch: Epoch, sufficient_support: bool) = - ## Check finalization on rule 4 "12" - doAssert epoch > 2 - getStateField(state, slot) = Slot((epoch * SLOTS_PER_EPOCH) - 1) # Skip ahead to just before epoch - - # 43210 -- epochs ago - # 210xx -- justification bitfields indices preshift - # 3210x -- justification bitfield indices postshift - # 01*0. -- justification bitfield contents. . = this epoch, * is being justified now - - # checkpoints for epochs ago - let (c1, c2, _, _, _) = getCheckpoints(epoch) - putCheckpointsInBlockRoots(state.phase0Data.data, [c1, c2]) - - # Save for final checks - let old_finalized = getStateField(state, finalized_checkpoint) - - # Mock the state - getStateField(state, previous_justified_checkpoint) = c2 - getStateField(state, current_justified_checkpoint) = c2 - getStateField(state, justification_bits) = JustificationBits(0'u8) # Bitvector of length 4 - # mock 3rd as justified - # indices are pre-shift - uint8(getStateField(state, justification_bits)).setBit 0 - # mock the 2nd latest epoch as justifiable, with 3rd as the source - addMockAttestations( - state.phase0Data.data, - epoch = epoch - 1, - source = c2, - target = c1, - sufficient_support = sufficient_support - ) - - # State transition - transitionEpochUntilJustificationFinalization(state) - - # Checks - doAssert getStateField(state, previous_justified_checkpoint) == c2 # changed to old current - if sufficient_support: - doAssert getStateField(state, current_justified_checkpoint) == c1 # changed to second latest - doAssert getStateField(state, finalized_checkpoint) == c2 # finalized old previous justified epoch - else: - doAssert getStateField(state, current_justified_checkpoint) == c2 # still old current - doAssert getStateField(state, finalized_checkpoint) == old_finalized # no new finalized checkpoint - -proc payload = - suite "[Unit - Spec - Epoch processing] Justification and Finalization " & preset(): - echo " Finalization rules are detailed at https://github.com/protolambda/eth2-docs#justification-and-finalization" - - const NumValidators = uint64(8) * SLOTS_PER_EPOCH - let genesisState = initGenesisState(NumValidators) - doAssert getStateField(genesisState[], validators).lenu64 == NumValidators - - setup: - let state = assignClone(genesisState[]) - - test " Rule I - 234 finalization with enough support": - finalizeOn234(state[], Epoch 5, sufficient_support = true) - - test " Rule I - 234 finalization without support": - finalizeOn234(state[], Epoch 5, sufficient_support = false) - - test " Rule II - 23 finalization with enough support": - finalizeOn23(state[], Epoch 4, sufficient_support = true) - - test " Rule II - 23 finalization without support": - finalizeOn23(state[], Epoch 4, sufficient_support = false) - - test " Rule III - 123 finalization with enough support": - finalizeOn123(state[], Epoch 6, sufficient_support = true) - - test " Rule III - 123 finalization without support": - finalizeOn123(state[], Epoch 6, sufficient_support = false) - - test " Rule IV - 12 finalization with enough support": - finalizeOn12(state[], Epoch 3, sufficient_support = true) - - test " Rule IV - 12 finalization without support": - finalizeOn12(state[], Epoch 3, sufficient_support = false) - -payload()