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:
parent
cb5454c4eb
commit
68654848cb
|
@ -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
|
||||
|
|
|
@ -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),
|
||||
)
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) =
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue