update get_attesting_indices(...) to 0.9.0; remove Crosslink from AttestationData to update AttestationData to 0.9.0; rm get_winning_crosslink_and_attesting_indices(...) and get_crosslink_deltas(...)

This commit is contained in:
Dustin Brody 2019-11-08 01:12:24 +01:00
parent cb5454c4eb
commit 68654848cb
7 changed files with 51 additions and 202 deletions

View File

@ -51,10 +51,11 @@ proc validate(
# was slashed in the same epoch - there's no penalty for doing this and
# the vote counting logic will take care of any ill effects (TODO verify)
let data = attestation.data
if not (data.crosslink.shard < SHARD_COUNT):
notice "Attestation shard too high",
attestation_shard = data.crosslink.shard
return
# TODO re-enable check
#if not (data.crosslink.shard < SHARD_COUNT):
# notice "Attestation shard too high",
# attestation_shard = data.crosslink.shard
# return
# Without this check, we can't get a slot number for the attestation as
# certain helpers will assert

View File

@ -329,14 +329,11 @@ func get_attestation_data_slot*(state: BeaconState,
data: AttestationData, committee_count: uint64): Slot =
# 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
(epoch, shard) = get_epoch_and_shard(state, data.slot, data.index)
offset = (shard + SHARD_COUNT -
get_start_shard(state, epoch)) mod SHARD_COUNT
doAssert data.crosslink.shard == shard
doAssert data.target.epoch == epoch
compute_start_slot_at_epoch(data.target.epoch) + offset div
compute_start_slot_at_epoch(epoch) + offset div
(committee_count div SLOTS_PER_EPOCH)
# This is the slower (O(n)), spec-compatible signature.
@ -469,7 +466,7 @@ proc is_valid_indexed_attestation*(
if not result:
notice "indexed attestation: signature verification failure"
# https://github.com/ethereum/eth2.0-specs/blob/v0.8.4/specs/core/0_beacon-chain.md#get_attesting_indices
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.0/specs/core/0_beacon-chain.md#get_attesting_indices
func get_attesting_indices*(state: BeaconState,
data: AttestationData,
bits: CommitteeValidatorsBits,
@ -477,9 +474,7 @@ func get_attesting_indices*(state: BeaconState,
HashSet[ValidatorIndex] =
# Return the set of attesting indices corresponding to ``data`` and ``bits``.
result = initSet[ValidatorIndex]()
let committee =
get_crosslink_committee(
state, data.target.epoch, data.crosslink.shard, stateCache)
let committee = get_beacon_committee(state, data.slot, data.index, stateCache)
for i, index in committee:
if bits[i]:
result.incl index
@ -548,11 +543,6 @@ proc check_attestation*(
trace "process_attestation: beginning",
attestation=attestation
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")
@ -572,19 +562,19 @@ proc check_attestation*(
state_slot = shortLog(stateSlot))
return
let committee = get_crosslink_committee(state, data.target.epoch, data.crosslink.shard, stateCache)
if attestation.aggregation_bits.len != attestation.custody_bits.len:
warn("Inconsistent aggregation and custody bits",
aggregation_bits_len = attestation.aggregation_bits.len,
custody_bits_len = attestation.custody_bits.len
)
return
if attestation.aggregation_bits.len != committee.len:
warn("Inconsistent aggregation and committee length",
aggregation_bits_len = attestation.aggregation_bits.len,
committee_len = committee.len
)
return
#let committee = get_crosslink_committee(state, data.target.epoch, data.crosslink.shard, stateCache)
#if attestation.aggregation_bits.len != attestation.custody_bits.len:
# warn("Inconsistent aggregation and custody bits",
# aggregation_bits_len = attestation.aggregation_bits.len,
# custody_bits_len = attestation.custody_bits.len
# )
# return
#if attestation.aggregation_bits.len != committee.len:
# warn("Inconsistent aggregation and committee length",
# aggregation_bits_len = attestation.aggregation_bits.len,
# committee_len = committee.len
# )
# return
# Check FFG data, crosslink data, and signature
let ffg_check_data = (data.source.epoch, data.source.root, data.target.epoch)
@ -670,12 +660,5 @@ proc makeAttestationData*(
target: Checkpoint(
epoch: current_epoch,
root: epoch_boundary_block_root
),
crosslink: Crosslink(
shard: shard,
parent_root: hash_tree_root(state.current_crosslinks[shard]),
start_epoch: parent_crosslink_end_epoch,
end_epoch: min(
current_epoch, parent_crosslink_end_epoch + MAX_EPOCHS_PER_CROSSLINK),
)
)

View File

@ -124,7 +124,7 @@ type
epoch*: Epoch
root*: Eth2Digest
# https://github.com/ethereum/eth2.0-specs/blob/v0.8.4/specs/core/0_beacon-chain.md#AttestationData
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.0/specs/core/0_beacon-chain.md#AttestationData
AttestationData* = object
slot*: Slot
index*: uint64
@ -136,9 +136,6 @@ type
source*: Checkpoint
target*: Checkpoint
# Crosslink vote
crosslink*: Crosslink
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.0/specs/core/0_beacon-chain.md#attestationdataandcustodybit
AttestationDataAndCustodyBit* = object
data*: AttestationData
@ -720,8 +717,7 @@ func shortLog*(v: AttestationData): auto =
source_epoch: shortLog(v.source.epoch),
source_root: shortLog(v.source.root),
target_epoch: shortLog(v.target.epoch),
target_root: shortLog(v.target.root),
crosslink: shortLog(v.crosslink)
target_root: shortLog(v.target.root)
)
chronicles.formatIt Slot: it.shortLog

View File

@ -92,103 +92,6 @@ func get_attesting_balance(
get_total_balance(state, get_unslashed_attesting_indices(
state, attestations, stateCache))
# Not exactly in spec, but for get_winning_crosslink_and_attesting_indices
func lowerThan(candidate, current: Eth2Digest): bool =
# return true iff candidate is "lower" than current, per spec rule:
# "ties broken in favor of lexicographically higher hash
for i, v in current.data:
if v > candidate.data[i]: return true
false
func get_winning_crosslink_and_attesting_indices(
state: BeaconState, epoch: Epoch, shard: Shard,
stateCache: var StateCache): tuple[a: Crosslink, b: HashSet[ValidatorIndex]] =
let
attestations =
filterIt(
get_matching_source_attestations(state, epoch),
it.data.crosslink.shard == shard)
root_current_shard_crosslink =
hash_tree_root(state.current_crosslinks[shard])
crosslinks =
filterIt(
mapIt(attestations, it.data.crosslink),
root_current_shard_crosslink == it.parent_root or
root_current_shard_crosslink == hash_tree_root(it))
# default=Crosslink()
if len(crosslinks) == 0:
return (Crosslink(), initSet[ValidatorIndex]())
## Not from spec. Don't repeatedly search/filter attestations in an O(n^2)
## way, but create lookup table in O(n) time with O(1) lookup by crosslink
## to cut out expensive inner loop.
##
## Could also sort attestations by .data.crosslink first, and rely on that
## ordering, among other approaches which don't change this function sig.
var attesting_indices = initTable[Eth2Digest, HashSet[ValidatorIndex]]()
for attestation in attestations:
let
crosslink = attestation.data.crosslink
crosslink_key = crosslink.data_root
var crosslink_attestation_indices =
if crosslink_key in attesting_indices:
attesting_indices[crosslink_key]
else:
initSet[ValidatorIndex]()
## See also how get_attesting_balance(...) works. This inverts the loop
## nesting order. Also, this ensures no duplicate indices, though it is
## not supposed to happen, regardless, if validators are only attesting
## on their assigned shards. Still, the right response there is slashed
## balances, not crashing clients.
crosslink_attestation_indices.incl(
get_unslashed_attesting_indices(state, [attestation], stateCache))
attesting_indices[crosslink_key] = crosslink_attestation_indices
## Winning crosslink has the crosslink data root with the most balance voting
## for it (ties broken lexicographically)
var
winning_crosslink: Crosslink
winning_crosslink_balance = 0.Gwei
for candidate_crosslink in crosslinks:
## TODO when confident this exactly reproduces the spec version,
## remove the when false'd scaffolding.
when false:
let crosslink_balance_uncached =
get_attesting_balance(
state,
filterIt(attestations, it.data.crosslink == candidate_crosslink),
stateCache)
# TODO verify if one can assume this cached balance always exists here, by
# doAsserting candidate_crosslink_key in attesting_indices
let
candidate_crosslink_key = candidate_crosslink.data_root
crosslink_balance =
if candidate_crosslink_key in attesting_indices:
get_total_balance(state, attesting_indices[candidate_crosslink_key])
else:
## See `get_total_balance(...)`
## But see above, this branch might never happen.
1.Gwei
## TODO factor out precalculation mechanism; consider adding compilation
## flag to enable long calculation & consistency/assumption checking.
when false:
doAssert crosslink_balance == crosslink_balance_uncached
if (crosslink_balance > winning_crosslink_balance or
(winning_crosslink_balance == crosslink_balance and
lowerThan(winning_crosslink.data_root,
candidate_crosslink.data_root))):
winning_crosslink = candidate_crosslink
winning_crosslink_balance = crosslink_balance
let winning_attestations =
filterIt(attestations, it.data.crosslink == winning_crosslink)
(winning_crosslink,
get_unslashed_attesting_indices(state, winning_attestations, stateCache))
# https://github.com/ethereum/eth2.0-specs/blob/v0.8.4/specs/core/0_beacon-chain.md#justification-and-finalization
proc process_justification_and_finalization*(
state: var BeaconState, stateCache: var StateCache) =
@ -426,48 +329,17 @@ func get_attestation_deltas(state: BeaconState, stateCache: var StateCache):
(rewards, penalties)
# https://github.com/ethereum/eth2.0-specs/blob/v0.8.4/specs/core/0_beacon-chain.md#rewards-and-penalties-1
func get_crosslink_deltas*(state: BeaconState, cache: var StateCache):
tuple[a: seq[Gwei], b: seq[Gwei]] =
var
rewards = repeat(0'u64, len(state.validators))
penalties = repeat(0'u64, len(state.validators))
let epoch = get_previous_epoch(state)
for offset in 0'u64 ..< get_committee_count(state, epoch):
let
shard = (get_start_shard(state, epoch) + offset) mod SHARD_COUNT
crosslink_committee =
toSet(get_crosslink_committee(state, epoch, shard, cache))
(_, attesting_indices) =
get_winning_crosslink_and_attesting_indices(
state, epoch, shard, cache)
attesting_balance = get_total_balance(state, attesting_indices)
committee_balance = get_total_balance(state, crosslink_committee)
for index in crosslink_committee:
let base_reward = get_base_reward(state, index)
if index in attesting_indices:
rewards[index] +=
base_reward * attesting_balance div committee_balance
else:
penalties[index] += base_reward
(rewards, penalties)
# https://github.com/ethereum/eth2.0-specs/blob/v0.8.4/specs/core/0_beacon-chain.md#rewards-and-penalties-1
func process_rewards_and_penalties(
state: var BeaconState, cache: var StateCache) =
if get_current_epoch(state) == GENESIS_EPOCH:
return
let
(rewards1, penalties1) = get_attestation_deltas(state, cache)
(rewards2, penalties2) = get_crosslink_deltas(state, cache)
let (rewards, penalties) = get_attestation_deltas(state, cache)
for i in 0 ..< len(state.validators):
increase_balance(state, i.ValidatorIndex, rewards1[i] + rewards2[i])
decrease_balance(state, i.ValidatorIndex, penalties1[i] + penalties2[i])
increase_balance(state, i.ValidatorIndex, rewards[i])
decrease_balance(state, i.ValidatorIndex, penalties[i])
# https://github.com/ethereum/eth2.0-specs/blob/v0.9.0/specs/core/0_beacon-chain.md#slashings
func process_slashings*(state: var BeaconState) =

View File

@ -61,12 +61,6 @@ proc mockAttestationData(
result.target = Checkpoint(
epoch: target_epoch, root: epoch_boundary_root
)
result.crosslink = Crosslink(
shard: shard,
start_epoch: parent_crosslink.end_epoch,
end_epoch: min(target_epoch, parent_crosslink.end_epoch + MAX_EPOCHS_PER_CROSSLINK),
parent_root: hash_tree_root(parent_crosslink)
)
proc get_attestation_signature(
state: BeaconState,
@ -125,10 +119,10 @@ proc mockAttestationImpl(
committees_per_slot * (slot mod SLOTS_PER_EPOCH)
) mod SHARD_COUNT
crosslink_committee = get_crosslink_committee(
crosslink_committee = get_beacon_committee(
state,
result.data.target.epoch,
result.data.crosslink.shard,
result.data.slot,
result.data.index,
cache
)
committee_size = crosslink_committee.len
@ -157,10 +151,10 @@ proc mockAttestation*(
proc fillAggregateAttestation*(state: BeaconState, attestation: var Attestation) =
var cache = get_empty_per_epoch_cache()
let crosslink_committee = get_crosslink_committee(
let crosslink_committee = get_beacon_committee(
state,
attestation.data.target.epoch,
attestation.data.crosslink.shard,
attestation.data.slot,
attestation.data.index,
cache
)
for i in 0 ..< crosslink_committee.len:

View File

@ -71,21 +71,22 @@ suite "[Unit - Spec - Block processing] Attestations " & preset():
nextEpoch(state)
applyEmptyBlock(state)
# TODO check if this should be replaced
when false:
when MAX_EPOCHS_PER_CROSSLINK > 4'u64:
test "Valid attestation since max epochs per crosslinks [Skipped for preset: " & const_preset & ']':
discard
else:
valid_attestation("Valid attestation since max epochs per crosslinks"):
for _ in 0 ..< MAX_EPOCHS_PER_CROSSLINK + 2:
nextEpoch(state)
applyEmptyBlock(state)
when MAX_EPOCHS_PER_CROSSLINK > 4'u64:
test "Valid attestation since max epochs per crosslinks [Skipped for preset: " & const_preset & ']':
discard
else:
valid_attestation("Valid attestation since max epochs per crosslinks"):
for _ in 0 ..< MAX_EPOCHS_PER_CROSSLINK + 2:
nextEpoch(state)
applyEmptyBlock(state)
let attestation = mockAttestation(state)
check: attestation.data.crosslink.end_epoch - attestation.data.crosslink.start_epoch == MAX_EPOCHS_PER_CROSSLINK
let attestation = mockAttestation(state)
check: attestation.data.crosslink.end_epoch - attestation.data.crosslink.start_epoch == MAX_EPOCHS_PER_CROSSLINK
for _ in 0 ..< MIN_ATTESTATION_INCLUSION_DELAY:
nextSlot(state)
for _ in 0 ..< MIN_ATTESTATION_INCLUSION_DELAY:
nextSlot(state)
valid_attestation("Empty aggregation bit"):
var attestation = mockAttestation(state)

View File

@ -80,13 +80,15 @@ proc addMockAttestations*(
if idx != -1:
aggregation_bits[idx] = false
let (ad_slot, ad_index) = get_slot_and_index(state, epoch, shard)
attestations[].add PendingAttestation(
aggregation_bits: aggregation_bits,
data: AttestationData(
slot: ad_slot,
index: ad_index,
beacon_block_root: [byte 0xFF] * 32, # Irrelevant for testing
source: source,
target: target,
crosslink: Crosslink(shard: shard)
),
inclusion_delay: 1
)