re-implement attestation checking in 0.8.0 form and rename relevant function from checkAttestation(...) to spec's process_attestation(...), though there's a slight difference in how they're factored; remove a couple of pointless type conversion warnings; rename validate_indexed_attestation(...) to is_valid_indexed_attestation(...); remove verify_bitfield(...) not in 0.8.0; update AttestationData to 0.8.0 by using Checkpoint data structure; rename MAX_CROSSLINK_EPOCHS to MAX_EPOCHS_PER_CROSSLINK

This commit is contained in:
Dustin Brody 2019-07-02 23:14:55 +02:00 committed by zah
parent d7905351eb
commit 8a5e5334d6
8 changed files with 89 additions and 86 deletions

View File

@ -297,7 +297,7 @@ proc getAttestationsForBlock*(
# attestations into the pool in general is an open question that needs # attestations into the pool in general is an open question that needs
# revisiting - for example, when attestations are added, against which # revisiting - for example, when attestations are added, against which
# state should they be validated, if at all? # state should they be validated, if at all?
if not checkAttestation( if not process_attestation(
state, attestation, {skipValidation, nextSlot}, cache): state, attestation, {skipValidation, nextSlot}, cache):
continue continue

View File

@ -146,7 +146,7 @@ func initiate_validator_exit*(state: var BeaconState,
# Set validator exit epoch and withdrawable epoch # Set validator exit epoch and withdrawable epoch
validator.exit_epoch = exit_queue_epoch validator.exit_epoch = exit_queue_epoch
validator.withdrawable_epoch = validator.withdrawable_epoch =
(validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY).Epoch validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY
# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#slash_validator # https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#slash_validator
func slash_validator*(state: var BeaconState, slashed_index: ValidatorIndex, func slash_validator*(state: var BeaconState, slashed_index: ValidatorIndex,
@ -259,16 +259,16 @@ func get_attestation_data_slot*(state: BeaconState,
# Return the slot corresponding to the attestation ``data``. # Return the slot corresponding to the attestation ``data``.
let let
offset = (data.crosslink.shard + SHARD_COUNT - offset = (data.crosslink.shard + SHARD_COUNT -
get_start_shard(state, data.target_epoch)) mod SHARD_COUNT get_start_shard(state, data.target.epoch)) mod SHARD_COUNT
(compute_start_slot_of_epoch(data.target_epoch) + offset div compute_start_slot_of_epoch(data.target.epoch) + offset div
(committee_count div SLOTS_PER_EPOCH)).Slot (committee_count div SLOTS_PER_EPOCH)
# This is the slower (O(n)), spec-compatible signature. # This is the slower (O(n)), spec-compatible signature.
func get_attestation_data_slot*(state: BeaconState, func get_attestation_data_slot*(state: BeaconState,
data: AttestationData): Slot = data: AttestationData): Slot =
get_attestation_data_slot( get_attestation_data_slot(
state, data, get_committee_count(state, data.target_epoch)) state, data, get_committee_count(state, data.target.epoch))
# https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#get_block_root_at_slot # https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#get_block_root_at_slot
func get_block_root_at_slot*(state: BeaconState, func get_block_root_at_slot*(state: BeaconState,
@ -332,8 +332,8 @@ func process_registry_updates*(state: var BeaconState) =
validator.activation_epoch = validator.activation_epoch =
get_delayed_activation_exit_epoch(get_current_epoch(state)) get_delayed_activation_exit_epoch(get_current_epoch(state))
# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#validate_indexed_attestation # https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#is_valid_indexed_attestation
func validate_indexed_attestation*( func is_valid_indexed_attestation*(
state: BeaconState, indexed_attestation: IndexedAttestation): bool = state: BeaconState, indexed_attestation: IndexedAttestation): bool =
# Verify validity of ``indexed_attestation`` fields. # Verify validity of ``indexed_attestation`` fields.
@ -379,7 +379,7 @@ func validate_indexed_attestation*(
get_domain( get_domain(
state, state,
DOMAIN_ATTESTATION, DOMAIN_ATTESTATION,
indexed_attestation.data.target_epoch indexed_attestation.data.target.epoch
), ),
) )
@ -398,9 +398,8 @@ func get_attesting_indices*(state: BeaconState,
result = initSet[ValidatorIndex]() result = initSet[ValidatorIndex]()
let committee = let committee =
get_crosslink_committee( get_crosslink_committee(
state, attestation_data.target_epoch, attestation_data.crosslink.shard, state, attestation_data.target.epoch, attestation_data.crosslink.shard,
stateCache) stateCache)
doAssert verify_bitfield(bitfield, len(committee))
for i, index in committee: for i, index in committee:
if get_bitfield_bit(bitfield, i): if get_bitfield_bit(bitfield, i):
result.incl index result.incl index
@ -462,11 +461,10 @@ func get_indexed_attestation(state: BeaconState, attestation: Attestation,
signature: attestation.signature, signature: attestation.signature,
) )
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#attestations # https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#attestations
proc checkAttestation*( proc process_attestation*(
state: BeaconState, attestation: Attestation, flags: UpdateFlags, state: BeaconState, attestation: Attestation, flags: UpdateFlags,
stateCache: var StateCache): bool = stateCache: var StateCache): bool =
## Process ``Attestation`` operation.
## Check that an attestation follows the rules of being included in the state ## Check that an attestation follows the rules of being included in the state
## at the current slot. When acting as a proposer, the same rules need to ## at the current slot. When acting as a proposer, the same rules need to
## be followed! ## be followed!
@ -475,9 +473,19 @@ proc checkAttestation*(
if nextSlot in flags: state.slot + 1 if nextSlot in flags: state.slot + 1
else: state.slot else: state.slot
let let data = attestation.data
data = attestation.data
attestation_slot = get_attestation_data_slot(state, attestation.data) if not (data.crosslink.shard < SHARD_COUNT):
warn("Attestation shard too high",
attestation_shard = data.crosslink.shard)
return
if not (data.target.epoch == get_previous_epoch(state) or
data.target.epoch == get_current_epoch(state)):
warn("Target epoch not current or previous epoch")
return
let attestation_slot = get_attestation_data_slot(state, attestation.data)
if not (attestation_slot + MIN_ATTESTATION_INCLUSION_DELAY <= stateSlot): if not (attestation_slot + MIN_ATTESTATION_INCLUSION_DELAY <= stateSlot):
warn("Attestation too new", warn("Attestation too new",
@ -498,25 +506,19 @@ proc checkAttestation*(
proposer_index: get_beacon_proposer_index(state, stateCache), proposer_index: get_beacon_proposer_index(state, stateCache),
) )
# Check target epoch, source epoch, source root, and source crosslink
if not (data.target_epoch == get_previous_epoch(state) or
data.target_epoch == get_current_epoch(state)):
warn("Target epoch not current or previous epoch")
return
# Check FFG data, crosslink data, and signature # Check FFG data, crosslink data, and signature
let ffg_check_data = (data.source_epoch, data.source_root, data.target_epoch) let ffg_check_data = (data.source.epoch, data.source.root, data.target.epoch)
if data.target_epoch == get_current_epoch(state): if data.target.epoch == get_current_epoch(state):
if not (ffg_check_data == (state.current_justified_epoch, if not (ffg_check_data == (state.current_justified_epoch,
state.current_justified_root, get_current_epoch(state))): state.current_justified_root, get_current_epoch(state))):
warn("FFG data not matching current justified epoch") warn("FFG data not matching current justified epoch")
return return
#if not (data.crosslink.parent_root == if not (data.crosslink.parent_root ==
# hash_tree_root(state.current_crosslinks[data.crosslink.shard])): hash_tree_root(state.current_crosslinks[data.crosslink.shard])):
# warn("Crosslink shard's current crosslinks not matching crosslink parent root") warn("Crosslink shard's current crosslinks not matching crosslink parent root")
# return return
#state.current_epoch_attestations.add(pending_attestation) #state.current_epoch_attestations.add(pending_attestation)
else: else:
@ -525,39 +527,49 @@ proc checkAttestation*(
warn("FFG data not matching current justified epoch") warn("FFG data not matching current justified epoch")
return return
#if not (data.crosslink.parent_root == if not (data.crosslink.parent_root ==
# hash_tree_root(state.previous_crosslinks[data.crosslink.shard])): hash_tree_root(state.previous_crosslinks[data.crosslink.shard])):
# warn("Crosslink shard's previous crosslinks not matching crosslink parent root") warn("Crosslink shard's previous crosslinks not matching crosslink parent root")
# return return
#state.previous_epoch_attestations.add(pending_attestation) #state.previous_epoch_attestations.add(pending_attestation)
# TODO un-comment when changed Crosslink to 0.7 structure let parent_crosslink = if data.target.epoch == get_current_epoch(state):
#if not (data.crosslink.start_epoch == parent_crosslink.end_epoch): state.current_crosslinks[data.crosslink.shard]
# warn("Crosslink start and end epochs not the same") else:
# return state.previous_crosslinks[data.crosslink.shard]
#if not (data.crosslink.end_epoch == min(data.target_epoch, parent_crosslink.end_epoch + MAX_EPOCHS_PER_CROSSLINK)): if not (data.crosslink.parent_root == hash_tree_root(parent_crosslink)):
# warn("Crosslink end epoch incorrect") warn("Crosslink parent root doesn't match parent crosslink's root")
# return return
# Simlarly, these depend on 0.7 data structures if not (data.crosslink.start_epoch == parent_crosslink.end_epoch):
#assert data.crosslink.parent_root == hash_tree_root(parent_crosslink) warn("Crosslink start and end epochs not the same")
return
#if not (data.crosslink.data_root == ZERO_HASH): # [to be removed in phase 1] if not (data.crosslink.end_epoch == min(
# warn("Crosslink data root not zero") data.target.epoch,
# return parent_crosslink.end_epoch + MAX_EPOCHS_PER_CROSSLINK)):
warn("Crosslink end epoch incorrect",
crosslink_end_epoch = data.crosslink.end_epoch,
parent_crosslink_end_epoch = parent_crosslink.end_epoch,
target_epoch = data.target.epoch)
return
if not (data.crosslink.data_root == ZERO_HASH): # [to be removed in phase 1]
warn("Crosslink data root not zero")
return
# Check signature and bitfields # Check signature and bitfields
if not validate_indexed_attestation( if not is_valid_indexed_attestation(
state, get_indexed_attestation(state, attestation, stateCache)): state, get_indexed_attestation(state, attestation, stateCache)):
warn("checkAttestation: signature or bitfields incorrect") warn("process_attestation: signature or bitfields incorrect")
return return
true true
proc makeAttestationData*( proc makeAttestationData*(
state: BeaconState, shard: uint64, state: BeaconState, shard_offset: uint64,
beacon_block_root: Eth2Digest): AttestationData = beacon_block_root: Eth2Digest): AttestationData =
## Fine points: ## Fine points:
## Head must be the head state during the slot that validator is ## Head must be the head state during the slot that validator is
@ -568,17 +580,23 @@ proc makeAttestationData*(
target_root = target_root =
if epoch_start_slot == state.slot: beacon_block_root if epoch_start_slot == state.slot: beacon_block_root
else: get_block_root_at_slot(state, epoch_start_slot) else: get_block_root_at_slot(state, epoch_start_slot)
shard = (shard_offset + get_start_shard(state,
compute_epoch_of_slot(state.slot))) mod SHARD_COUNT
target_epoch = compute_epoch_of_slot(state.slot)
AttestationData( AttestationData(
beacon_block_root: beacon_block_root, beacon_block_root: beacon_block_root,
target_root: target_root, source: Checkpoint(
source_epoch: state.current_justified_epoch, epoch: state.current_justified_epoch,
source_root: state.current_justified_root, root: state.current_justified_root
target_epoch: compute_epoch_of_slot(state.slot), ),
target: Checkpoint(
root: target_root,
epoch: target_epoch
),
crosslink: Crosslink( crosslink: Crosslink(
# Alternative is to put this offset into all callers shard: shard,
shard: shard + get_start_shard(state, compute_epoch_of_slot(state.slot)),
parent_root: hash_tree_root(state.current_crosslinks[shard]), parent_root: hash_tree_root(state.current_crosslinks[shard]),
data_root: Eth2Digest(), # Stub in phase0 end_epoch: target_epoch,
) )
) )

View File

@ -24,19 +24,6 @@ func get_bitfield_bit*(bitfield: BitField, i: int): bool =
doAssert i div 8 < bitfield.bits.len, "i: " & $i & " i div 8: " & $(i div 8) doAssert i div 8 < bitfield.bits.len, "i: " & $i & " i div 8: " & $(i div 8)
((bitfield.bits[i div 8] shr (i mod 8)) mod 2) > 0'u8 ((bitfield.bits[i div 8] shr (i mod 8)) mod 2) > 0'u8
# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#verify_bitfield
func verify_bitfield*(bitfield: BitField, committee_size: int): bool =
# Verify ``bitfield`` against the ``committee_size``.
if len(bitfield.bits) != (committee_size + 7) div 8:
return false
# Check `bitfield` is padded with zero bits only
for i in committee_size ..< (len(bitfield.bits) * 8):
if get_bitfield_bit(bitfield, i):
return false
true
# TODO spec candidatidates below, though they're used only indirectly there.. # TODO spec candidatidates below, though they're used only indirectly there..
func set_bitfield_bit*(bitfield: var BitField, i: int) = func set_bitfield_bit*(bitfield: var BitField, i: int) =
bitfield.bits[i div 8] = bitfield.bits[i div 8] or 1'u8 shl (i mod 8) bitfield.bits[i div 8] = bitfield.bits[i div 8] or 1'u8 shl (i mod 8)

View File

@ -123,16 +123,14 @@ type
epoch*: Epoch epoch*: Epoch
root*: Eth2Digest root*: Eth2Digest
# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#attestationdata # https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#AttestationData
AttestationData* = object AttestationData* = object
# LMD GHOST vote # LMD GHOST vote
beacon_block_root*: Eth2Digest beacon_block_root*: Eth2Digest
# FFG vote # FFG vote
source_epoch*: Epoch source*: Checkpoint
source_root*: Eth2Digest target*: Checkpoint
target_epoch*: Epoch
target_root*: Eth2Digest
# Crosslink vote # Crosslink vote
crosslink*: Crosslink crosslink*: Crosslink
@ -456,8 +454,8 @@ func shortLog*(v: BeaconBlock): tuple[
func shortLog*(v: AttestationData): auto = func shortLog*(v: AttestationData): auto =
( (
shortLog(v.beacon_block_root), shortLog(v.beacon_block_root),
humaneEpochNum(v.source_epoch), shortLog(v.target_root), humaneEpochNum(v.source.epoch), shortLog(v.target.root),
shortLog(v.source_root), shortLog(v.source.root),
v.crosslink v.crosslink
) )

View File

@ -128,7 +128,7 @@ const
PERSISTENT_COMMITTEE_PERIOD* = 2'u64^11 ##\ PERSISTENT_COMMITTEE_PERIOD* = 2'u64^11 ##\
## epochs (9 days) ## epochs (9 days)
MAX_CROSSLINK_EPOCHS* = 2'u64^6 ##\ MAX_EPOCHS_PER_CROSSLINK* = 2'u64^6 ##\
## epochs (~7 hours) ## epochs (~7 hours)
MIN_EPOCHS_TO_INACTIVITY_PENALTY* = 2'u64^2 ##\ MIN_EPOCHS_TO_INACTIVITY_PENALTY* = 2'u64^2 ##\

View File

@ -93,7 +93,7 @@ const
# Unchanged # Unchanged
MIN_VALIDATOR_WITHDRAWABILITY_DELAY* = 2'u64^8 MIN_VALIDATOR_WITHDRAWABILITY_DELAY* = 2'u64^8
PERSISTENT_COMMITTEE_PERIOD* = 2'u64^11 PERSISTENT_COMMITTEE_PERIOD* = 2'u64^11
MAX_CROSSLINK_EPOCHS* = 2'u64^6 MAX_EPOCHS_PER_CROSSLINK* = 2'u64^6
MIN_EPOCHS_TO_INACTIVITY_PENALTY* = 2'u64^2 MIN_EPOCHS_TO_INACTIVITY_PENALTY* = 2'u64^2
# State list lengths # State list lengths

View File

@ -184,10 +184,10 @@ func is_slashable_attestation_data(
## rules. ## rules.
# Double vote # Double vote
(data_1 != data_2 and data_1.target_epoch == data_2.target_epoch) or (data_1 != data_2 and data_1.target.epoch == data_2.target.epoch) or
# Surround vote # Surround vote
(data_1.source_epoch < data_2.source_epoch and (data_1.source.epoch < data_2.source.epoch and
data_2.target_epoch < data_1.target_epoch) data_2.target.epoch < data_1.target.epoch)
# https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#attester-slashings # https://github.com/ethereum/eth2.0-specs/blob/v0.7.1/specs/core/0_beacon-chain.md#attester-slashings
proc processAttesterSlashings(state: var BeaconState, blck: BeaconBlock, proc processAttesterSlashings(state: var BeaconState, blck: BeaconBlock,
@ -208,11 +208,11 @@ proc processAttesterSlashings(state: var BeaconState, blck: BeaconBlock,
notice "CaspSlash: surround or double vote check failed" notice "CaspSlash: surround or double vote check failed"
return false return false
if not validate_indexed_attestation(state, attestation_1): if not is_valid_indexed_attestation(state, attestation_1):
notice "CaspSlash: invalid votes 1" notice "CaspSlash: invalid votes 1"
return false return false
if not validate_indexed_attestation(state, attestation_2): if not is_valid_indexed_attestation(state, attestation_2):
notice "CaspSlash: invalid votes 2" notice "CaspSlash: invalid votes 2"
return false return false
@ -247,7 +247,7 @@ proc processAttestations(
notice "Attestation: too many!", attestations = blck.body.attestations.len notice "Attestation: too many!", attestations = blck.body.attestations.len
return false return false
if not blck.body.attestations.allIt(checkAttestation(state, it, flags, stateCache)): if not blck.body.attestations.allIt(process_attestation(state, it, flags, stateCache)):
return false return false
# All checks passed - update state # All checks passed - update state
@ -257,7 +257,7 @@ proc processAttestations(
for attestation in blck.body.attestations: for attestation in blck.body.attestations:
# Caching # Caching
let let
epoch = attestation.data.target_epoch epoch = attestation.data.target.epoch
committee_count = if epoch in committee_count_cache: committee_count = if epoch in committee_count_cache:
committee_count_cache[epoch] committee_count_cache[epoch]
else: else:
@ -274,7 +274,7 @@ proc processAttestations(
proposer_index: get_beacon_proposer_index(state, stateCache), proposer_index: get_beacon_proposer_index(state, stateCache),
) )
if attestation.data.target_epoch == get_current_epoch(state): if attestation.data.target.epoch == get_current_epoch(state):
state.current_epoch_attestations.add(pending_attestation) state.current_epoch_attestations.add(pending_attestation)
else: else:
state.previous_epoch_attestations.add(pending_attestation) state.previous_epoch_attestations.add(pending_attestation)

View File

@ -55,7 +55,7 @@ func get_matching_target_attestations(state: BeaconState, epoch: Epoch):
seq[PendingAttestation] = seq[PendingAttestation] =
filterIt( filterIt(
get_matching_source_attestations(state, epoch), get_matching_source_attestations(state, epoch),
it.data.target_root == get_block_root(state, epoch) it.data.target.root == get_block_root(state, epoch)
) )
func get_matching_head_attestations(state: BeaconState, epoch: Epoch): func get_matching_head_attestations(state: BeaconState, epoch: Epoch):