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
# revisiting - for example, when attestations are added, against which
# state should they be validated, if at all?
if not checkAttestation(
if not process_attestation(
state, attestation, {skipValidation, nextSlot}, cache):
continue

View File

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

View File

@ -123,16 +123,14 @@ type
epoch*: Epoch
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
# LMD GHOST vote
beacon_block_root*: Eth2Digest
# FFG vote
source_epoch*: Epoch
source_root*: Eth2Digest
target_epoch*: Epoch
target_root*: Eth2Digest
source*: Checkpoint
target*: Checkpoint
# Crosslink vote
crosslink*: Crosslink
@ -456,8 +454,8 @@ func shortLog*(v: BeaconBlock): tuple[
func shortLog*(v: AttestationData): auto =
(
shortLog(v.beacon_block_root),
humaneEpochNum(v.source_epoch), shortLog(v.target_root),
shortLog(v.source_root),
humaneEpochNum(v.source.epoch), shortLog(v.target.root),
shortLog(v.source.root),
v.crosslink
)

View File

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

View File

@ -93,7 +93,7 @@ const
# Unchanged
MIN_VALIDATOR_WITHDRAWABILITY_DELAY* = 2'u64^8
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
# State list lengths

View File

@ -184,10 +184,10 @@ func is_slashable_attestation_data(
## rules.
# 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
(data_1.source_epoch < data_2.source_epoch and
data_2.target_epoch < data_1.target_epoch)
(data_1.source.epoch < data_2.source.epoch and
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
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"
return false
if not validate_indexed_attestation(state, attestation_1):
if not is_valid_indexed_attestation(state, attestation_1):
notice "CaspSlash: invalid votes 1"
return false
if not validate_indexed_attestation(state, attestation_2):
if not is_valid_indexed_attestation(state, attestation_2):
notice "CaspSlash: invalid votes 2"
return false
@ -247,7 +247,7 @@ proc processAttestations(
notice "Attestation: too many!", attestations = blck.body.attestations.len
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
# All checks passed - update state
@ -257,7 +257,7 @@ proc processAttestations(
for attestation in blck.body.attestations:
# Caching
let
epoch = attestation.data.target_epoch
epoch = attestation.data.target.epoch
committee_count = if epoch in committee_count_cache:
committee_count_cache[epoch]
else:
@ -274,7 +274,7 @@ proc processAttestations(
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)
else:
state.previous_epoch_attestations.add(pending_attestation)

View File

@ -55,7 +55,7 @@ func get_matching_target_attestations(state: BeaconState, epoch: Epoch):
seq[PendingAttestation] =
filterIt(
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):