252 lines
10 KiB
Nim
252 lines
10 KiB
Nim
# 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()
|