0.6.3 updates (#279)
* update IndexedAttestation, verify_slashable_attestation/verify_indexed_attestation, and attester slashing processing to 0.6.3 * rm debug scaffolding * rename Attestation.aggregate_signature -> Attestation.signature; convert various references to AttestationData.slot to get_attestation_slot; implement convert_to_indexed; update checkAttestation and processAttestations to 0.6.3; remove spurious assertion in beacon node related to invalid attestations * replace 0.5 get_winning_root_and_participants with 0.6 get_winning_crosslink_and_attesting_indices; update process_crosslinks to 0.6.3 * mark both remaining 0.6.0 spec implementations as 0.6.3 * clear out all remaining spec version 0.6.1 refs * GENESIS_SLOT and GENESIS_EPOCH are 0 * rm 0.5 get_attestation_participants in favor of 0.6 get_attesting_indices * address mratsim's comment * time can be equal to genesis * fix invalid block assertions; those were essentially spurious * allow toBeaconTime to handle time before genesis (in accordance with now(...) which states time can exist before GENESIS)
This commit is contained in:
parent
10c7920b27
commit
d400650eeb
|
@ -27,7 +27,7 @@ proc combine*(tgt: var Attestation, src: Attestation, flags: UpdateFlags) =
|
|||
tgt.aggregation_bitfield.combine(src.aggregation_bitfield)
|
||||
|
||||
if skipValidation notin flags:
|
||||
tgt.aggregate_signature.combine(src.aggregate_signature)
|
||||
tgt.signature.combine(src.signature)
|
||||
|
||||
proc validate(
|
||||
state: BeaconState, attestation: Attestation, flags: UpdateFlags): bool =
|
||||
|
@ -38,7 +38,7 @@ proc validate(
|
|||
|
||||
# TODO half of this stuff is from beaconstate.validateAttestation - merge?
|
||||
|
||||
let attestationSlot = attestation.data.slot
|
||||
let attestationSlot = get_attestation_slot(state, attestation)
|
||||
|
||||
if attestationSlot < state.finalized_epoch.get_epoch_start_slot():
|
||||
debug "Old attestation",
|
||||
|
@ -71,7 +71,7 @@ proc validate(
|
|||
## the rest; turns into expensive NOP until then.
|
||||
if skipValidation notin flags:
|
||||
let
|
||||
participants = get_attestation_participants(
|
||||
participants = get_attesting_indices_seq(
|
||||
state, attestation.data, attestation.aggregation_bitfield)
|
||||
|
||||
## TODO when the custody_bitfield assertion-to-emptiness disappears do this
|
||||
|
@ -97,9 +97,9 @@ proc validate(
|
|||
hash_tree_root(AttestationDataAndCustodyBit(
|
||||
data: attestation.data, custody_bit: true)),
|
||||
],
|
||||
attestation.aggregate_signature,
|
||||
attestation.signature,
|
||||
get_domain(state, DOMAIN_ATTESTATION,
|
||||
slot_to_epoch(attestation.data.slot)),
|
||||
slot_to_epoch(get_attestation_slot(state, attestation))),
|
||||
):
|
||||
notice "Invalid signature", participants
|
||||
return false
|
||||
|
@ -174,14 +174,14 @@ proc add*(pool: var AttestationPool,
|
|||
# TODO inefficient data structures..
|
||||
|
||||
let
|
||||
attestationSlot = attestation.data.slot
|
||||
attestationSlot = get_attestation_slot(state, attestation)
|
||||
idx = pool.slotIndex(state, attestationSlot)
|
||||
slotData = addr pool.slots[idx]
|
||||
validation = Validation(
|
||||
aggregation_bitfield: attestation.aggregation_bitfield,
|
||||
custody_bitfield: attestation.custody_bitfield,
|
||||
aggregate_signature: attestation.aggregate_signature)
|
||||
participants = get_attestation_participants(
|
||||
aggregate_signature: attestation.signature)
|
||||
participants = get_attesting_indices_seq(
|
||||
state, attestation.data, validation.aggregation_bitfield)
|
||||
|
||||
var found = false
|
||||
|
@ -196,7 +196,7 @@ proc add*(pool: var AttestationPool,
|
|||
# sets by virtue of not overlapping with some other attestation
|
||||
# and therefore being useful after all?
|
||||
debug "Ignoring subset attestation",
|
||||
existingParticipants = get_attestation_participants(
|
||||
existingParticipants = get_attesting_indices_seq(
|
||||
state, a.data, v.aggregation_bitfield),
|
||||
newParticipants = participants
|
||||
found = true
|
||||
|
@ -209,7 +209,7 @@ proc add*(pool: var AttestationPool,
|
|||
if it.aggregation_bitfield.isSubsetOf(
|
||||
validation.aggregation_bitfield):
|
||||
debug "Removing subset attestation",
|
||||
existingParticipants = get_attestation_participants(
|
||||
existingParticipants = get_attesting_indices_seq(
|
||||
state, a.data, it.aggregation_bitfield),
|
||||
newParticipants = participants
|
||||
false
|
||||
|
@ -287,7 +287,7 @@ proc getAttestationsForBlock*(
|
|||
aggregation_bitfield: a.validations[0].aggregation_bitfield,
|
||||
data: a.data,
|
||||
custody_bitfield: a.validations[0].custody_bitfield,
|
||||
aggregate_signature: a.validations[0].aggregate_signature
|
||||
signature: a.validations[0].aggregate_signature
|
||||
)
|
||||
|
||||
# TODO what's going on here is that when producing a block, we need to
|
||||
|
@ -312,7 +312,7 @@ proc getAttestationsForBlock*(
|
|||
attestation.aggregation_bitfield.combine(
|
||||
v.aggregation_bitfield)
|
||||
attestation.custody_bitfield.combine(v.custody_bitfield)
|
||||
attestation.aggregate_signature.combine(v.aggregate_signature)
|
||||
attestation.signature.combine(v.aggregate_signature)
|
||||
|
||||
result.add(attestation)
|
||||
|
||||
|
@ -324,7 +324,8 @@ proc resolve*(pool: var AttestationPool, state: BeaconState) =
|
|||
var resolved: seq[Attestation]
|
||||
|
||||
for k, v in pool.unresolved.mpairs():
|
||||
if v.tries > 8 or v.attestation.data.slot < pool.startingSlot:
|
||||
let attestation_slot = get_attestation_slot(state, v.attestation)
|
||||
if v.tries > 8 or attestation_slot < pool.startingSlot:
|
||||
done.add(k)
|
||||
else:
|
||||
if pool.blockPool.get(k).isSome():
|
||||
|
|
|
@ -314,7 +314,7 @@ proc sendAttestation(node: BeaconNode,
|
|||
|
||||
var attestation = Attestation(
|
||||
data: attestationData,
|
||||
aggregate_signature: validatorSignature,
|
||||
signature: validatorSignature,
|
||||
aggregation_bitfield: aggregationBitfield,
|
||||
# Stub in phase0
|
||||
custody_bitfield: BitField.init(committeeLen)
|
||||
|
@ -371,7 +371,9 @@ proc proposeBlock(node: BeaconNode,
|
|||
var tmpState = hashedState
|
||||
|
||||
let ok = updateState(tmpState, newBlock, {skipValidation})
|
||||
doAssert ok # TODO: err, could this fail somehow?
|
||||
# TODO only enable in fast-fail debugging situations
|
||||
# otherwise, bad attestations can bring down network
|
||||
# doAssert ok # TODO: err, could this fail somehow?
|
||||
|
||||
newBlock.state_root = tmpState.root
|
||||
|
||||
|
@ -405,7 +407,7 @@ proc onAttestation(node: BeaconNode, attestation: Attestation) =
|
|||
# we're on, or that it follows the rules of the protocol
|
||||
debug "Attestation received",
|
||||
attestationData = shortLog(attestation.data),
|
||||
signature = shortLog(attestation.aggregate_signature)
|
||||
signature = shortLog(attestation.signature)
|
||||
|
||||
# TODO seems reasonable to use the latest head state here.. needs thinking
|
||||
# though - maybe we should use the state from the block pointed to by
|
||||
|
|
|
@ -6,12 +6,12 @@
|
|||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import
|
||||
algorithm, chronicles, math, options, sequtils,
|
||||
algorithm, chronicles, collections/sets, math, options, sequtils,
|
||||
../extras, ../ssz, ../beacon_node_types,
|
||||
./bitfield, ./crypto, ./datatypes, ./digest, ./helpers, ./validator,
|
||||
tables
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.0/specs/core/0_beacon-chain.md#verify_merkle_branch
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#verify_merkle_branch
|
||||
func verify_merkle_branch(leaf: Eth2Digest, proof: openarray[Eth2Digest], depth: uint64, index: uint64, root: Eth2Digest): bool =
|
||||
## Verify that the given ``leaf`` is on the merkle branch ``proof``
|
||||
## starting with the given ``root``.
|
||||
|
@ -153,7 +153,7 @@ func initiate_validator_exit*(state: var BeaconState,
|
|||
validator.withdrawable_epoch =
|
||||
validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#slash_validator
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#slash_validator
|
||||
func slash_validator*(state: var BeaconState, slashed_index: ValidatorIndex) =
|
||||
# Slash the validator with index ``index``.
|
||||
let current_epoch = get_current_epoch(state)
|
||||
|
@ -168,6 +168,7 @@ func slash_validator*(state: var BeaconState, slashed_index: ValidatorIndex) =
|
|||
|
||||
let
|
||||
proposer_index = get_beacon_proposer_index(state)
|
||||
# Spec has whistleblower_index as optional param, but it's never used.
|
||||
whistleblower_index = proposer_index
|
||||
whistleblowing_reward = slashed_balance div WHISTLEBLOWING_REWARD_QUOTIENT
|
||||
proposer_reward = whistleblowing_reward div PROPOSER_REWARD_QUOTIENT
|
||||
|
@ -176,8 +177,8 @@ func slash_validator*(state: var BeaconState, slashed_index: ValidatorIndex) =
|
|||
state, whistleblower_index, whistleblowing_reward - proposer_reward)
|
||||
decrease_balance(state, slashed_index, whistleblowing_reward)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.1/specs/core/0_beacon-chain.md#get_temporary_block_header
|
||||
func get_temporary_block_header*(blck: BeaconBlock): BeaconBlockHeader =
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.1/specs/core/0_beacon-chain.md#on-genesis
|
||||
func get_temporary_block_header(blck: BeaconBlock): BeaconBlockHeader =
|
||||
## Return the block header corresponding to a block with ``state_root`` set
|
||||
## to ``ZERO_HASH``.
|
||||
BeaconBlockHeader(
|
||||
|
@ -186,10 +187,9 @@ func get_temporary_block_header*(blck: BeaconBlock): BeaconBlockHeader =
|
|||
state_root: ZERO_HASH,
|
||||
block_body_root: hash_tree_root(blck.body),
|
||||
# signing_root(block) is used for block id purposes so signature is a stub
|
||||
signature: EMPTY_SIGNATURE,
|
||||
signature: ValidatorSig(),
|
||||
)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.1/specs/core/0_beacon-chain.md#on-genesis
|
||||
func get_empty_block*(): BeaconBlock =
|
||||
# Nim default values fill this in mostly correctly.
|
||||
BeaconBlock(slot: GENESIS_SLOT)
|
||||
|
@ -283,7 +283,7 @@ func get_initial_beacon_block*(state: BeaconState): BeaconBlock =
|
|||
# initialized to default values.
|
||||
)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#get_attestation_slot
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.2/specs/core/0_beacon-chain.md#get_attestation_slot
|
||||
func get_attestation_slot*(state: BeaconState,
|
||||
attestation: Attestation|PendingAttestation,
|
||||
committee_count: uint64): Slot =
|
||||
|
@ -291,7 +291,10 @@ func get_attestation_slot*(state: BeaconState,
|
|||
epoch = attestation.data.target_epoch
|
||||
offset = (attestation.data.shard + SHARD_COUNT -
|
||||
get_epoch_start_shard(state, epoch)) mod SHARD_COUNT
|
||||
result = get_epoch_start_slot(epoch) + offset div (committee_count div SLOTS_PER_EPOCH)
|
||||
|
||||
# TODO re-instate once double-check correct conditions in attestation pool
|
||||
#get_epoch_start_slot(epoch) + offset div (committee_count div SLOTS_PER_EPOCH)
|
||||
attestation.data.slot
|
||||
|
||||
# This is the slower (O(n)), spec-compatible signature.
|
||||
func get_attestation_slot*(state: BeaconState,
|
||||
|
@ -314,81 +317,6 @@ func get_block_root*(state: BeaconState, epoch: Epoch): Eth2Digest =
|
|||
# Return the block root at a recent ``epoch``.
|
||||
get_block_root_at_slot(state, get_epoch_start_slot(epoch))
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.1/specs/core/0_beacon-chain.md#get_attestation_participants
|
||||
func get_attestation_participants*(state: BeaconState,
|
||||
attestation_data: AttestationData,
|
||||
bitfield: BitField): seq[ValidatorIndex] =
|
||||
## Return the participant indices at for the ``attestation_data`` and
|
||||
## ``bitfield``.
|
||||
## Attestation participants in the attestation data are called out in a
|
||||
## bit field that corresponds to the committee of the shard at the time;
|
||||
## this function converts it to list of indices in to BeaconState.validators
|
||||
##
|
||||
## Returns empty list if the shard is not found
|
||||
## Return the participant indices at for the ``attestation_data`` and ``bitfield``.
|
||||
##
|
||||
# TODO Linear search through shard list? borderline ok, it's a small list
|
||||
# TODO iterator candidate
|
||||
|
||||
# Find the committee in the list with the desired shard
|
||||
let crosslink_committees = get_crosslink_committees_at_slot(
|
||||
state, attestation_data.slot)
|
||||
|
||||
doAssert anyIt(
|
||||
crosslink_committees,
|
||||
it[1] == attestation_data.shard)
|
||||
let crosslink_committee = mapIt(
|
||||
filterIt(crosslink_committees, it.shard == attestation_data.shard),
|
||||
it.committee)[0]
|
||||
|
||||
# TODO this and other attestation-based fields need validation so we don't
|
||||
# crash on a malicious attestation!
|
||||
doAssert verify_bitfield(bitfield, len(crosslink_committee))
|
||||
|
||||
# Find the participating attesters in the committee
|
||||
result = @[]
|
||||
for i, validator_index in crosslink_committee:
|
||||
let aggregation_bit = get_bitfield_bit(bitfield, i)
|
||||
if aggregation_bit:
|
||||
result.add(validator_index)
|
||||
|
||||
iterator get_attestation_participants_cached*(state: BeaconState,
|
||||
attestation_data: AttestationData,
|
||||
bitfield: BitField,
|
||||
cache: var StateCache): ValidatorIndex =
|
||||
## Return the participant indices at for the ``attestation_data`` and
|
||||
## ``bitfield``.
|
||||
## Attestation participants in the attestation data are called out in a
|
||||
## bit field that corresponds to the committee of the shard at the time;
|
||||
## this function converts it to list of indices in to BeaconState.validators
|
||||
##
|
||||
## Returns empty list if the shard is not found
|
||||
## Return the participant indices at for the ``attestation_data`` and ``bitfield``.
|
||||
##
|
||||
# TODO Linear search through shard list? borderline ok, it's a small list
|
||||
# TODO iterator candidate
|
||||
|
||||
# Find the committee in the list with the desired shard
|
||||
# let crosslink_committees = get_crosslink_committees_at_slot_cached(
|
||||
# state, attestation_data.slot, false, crosslink_committees_cached)
|
||||
|
||||
var found = false
|
||||
for crosslink_committee in get_crosslink_committees_at_slot_cached(
|
||||
state, attestation_data.slot, cache):
|
||||
if crosslink_committee.shard == attestation_data.shard:
|
||||
# TODO this and other attestation-based fields need validation so we don't
|
||||
# crash on a malicious attestation!
|
||||
doAssert verify_bitfield(bitfield, len(crosslink_committee.committee))
|
||||
|
||||
# Find the participating attesters in the committee
|
||||
for i, validator_index in crosslink_committee.committee:
|
||||
let aggregation_bit = get_bitfield_bit(bitfield, i)
|
||||
if aggregation_bit:
|
||||
yield validator_index
|
||||
found = true
|
||||
break
|
||||
doAssert found, "Couldn't find crosslink committee"
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#get_total_balance
|
||||
func get_total_balance*(state: BeaconState, validators: auto): Gwei =
|
||||
# Return the combined effective balance of an array of ``validators``.
|
||||
|
@ -432,9 +360,133 @@ 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.5.1/specs/core/0_beacon-chain.md#attestations
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#verify_indexed_attestation
|
||||
func verify_indexed_attestation*(
|
||||
state: BeaconState, indexed_attestation: IndexedAttestation): bool =
|
||||
# Verify validity of ``indexed_attestation`` fields.
|
||||
|
||||
let
|
||||
custody_bit_0_indices = indexed_attestation.custody_bit_0_indices
|
||||
custody_bit_1_indices = indexed_attestation.custody_bit_1_indices
|
||||
|
||||
# Ensure no duplicate indices across custody bits
|
||||
if len(intersection(toSet(custody_bit_0_indices), toSet(custody_bit_1_indices))) != 0:
|
||||
return false
|
||||
|
||||
if len(custody_bit_1_indices) > 0: # [TO BE REMOVED IN PHASE 1]
|
||||
return false
|
||||
|
||||
let combined_len = len(custody_bit_0_indices) + len(custody_bit_1_indices)
|
||||
if not (1 <= combined_len and combined_len <= MAX_INDICES_PER_ATTESTATION):
|
||||
return false
|
||||
|
||||
if custody_bit_0_indices != sorted(custody_bit_0_indices, system.cmp):
|
||||
return false
|
||||
|
||||
if custody_bit_1_indices != sorted(custody_bit_1_indices, system.cmp):
|
||||
return false
|
||||
|
||||
bls_verify_multiple(
|
||||
@[
|
||||
bls_aggregate_pubkeys(
|
||||
mapIt(custody_bit_0_indices, state.validator_registry[it.int].pubkey)),
|
||||
bls_aggregate_pubkeys(
|
||||
mapIt(custody_bit_1_indices, state.validator_registry[it.int].pubkey)),
|
||||
],
|
||||
@[
|
||||
hash_tree_root(AttestationDataAndCustodyBit(
|
||||
data: indexed_attestation.data, custody_bit: false)),
|
||||
hash_tree_root(AttestationDataAndCustodyBit(
|
||||
data: indexed_attestation.data, custody_bit: true)),
|
||||
],
|
||||
indexed_attestation.signature,
|
||||
get_domain(
|
||||
state,
|
||||
DOMAIN_ATTESTATION,
|
||||
indexed_attestation.data.target_epoch
|
||||
),
|
||||
)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#get_attesting_indices
|
||||
func get_attesting_indices*(state: BeaconState,
|
||||
attestation_data: AttestationData,
|
||||
bitfield: BitField): HashSet[ValidatorIndex] =
|
||||
## Return the sorted attesting indices corresponding to ``attestation_data``
|
||||
## and ``bitfield``.
|
||||
## The spec goes through a lot of hoops to sort things, and sometimes
|
||||
## constructs sets from the results here. The basic idea is to always
|
||||
## just keep it in a HashSet, which seems to suffice. If needed, it's
|
||||
## possible to follow the spec more literally.
|
||||
result = initSet[ValidatorIndex]()
|
||||
let committee =
|
||||
get_crosslink_committee(state, attestation_data.target_epoch,
|
||||
attestation_data.shard)
|
||||
doAssert verify_bitfield(bitfield, len(committee))
|
||||
for i, index in committee:
|
||||
if get_bitfield_bit(bitfield, i):
|
||||
result.incl index
|
||||
|
||||
func get_attesting_indices_seq*(
|
||||
state: BeaconState, attestation_data: AttestationData, bitfield: BitField):
|
||||
seq[ValidatorIndex] =
|
||||
toSeq(items(get_attesting_indices(state, attestation_data, bitfield)))
|
||||
|
||||
# TODO legacy function name; rename, reimplement caching if useful, blob/v0.6.2
|
||||
iterator get_attestation_participants_cached*(
|
||||
state: BeaconState, attestation_data: AttestationData, bitfield: BitField,
|
||||
cache: var StateCache): ValidatorIndex =
|
||||
for participant in get_attesting_indices(state, attestation_data, bitfield):
|
||||
yield participant
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#convert_to_indexed
|
||||
func convert_to_indexed(state: BeaconState, attestation: Attestation): IndexedAttestation =
|
||||
# Convert ``attestation`` to (almost) indexed-verifiable form.
|
||||
let
|
||||
attesting_indices =
|
||||
get_attesting_indices(
|
||||
state, attestation.data, attestation.aggregation_bitfield)
|
||||
custody_bit_1_indices =
|
||||
get_attesting_indices(
|
||||
state, attestation.data, attestation.custody_bitfield)
|
||||
|
||||
## TODO quadratic, .items, but first-class iterators, etc
|
||||
## filterIt can't work on HashSets directly because it is
|
||||
## assuming int-indexable thing to extract type, because,
|
||||
## like lots of other things in sequtils, it's a template
|
||||
## which doesn't otherwise care about the type system. It
|
||||
## is a mess. Just write the for-loop, etc, I guess, is a
|
||||
## reasonable reaction because of the special for binding
|
||||
## with (non-closure, etc) iterators no other part of Nim
|
||||
## can access. As such, this function's doing many copies
|
||||
## and allocations it has no fundamental reason to do.
|
||||
custody_bit_0_indices =
|
||||
filterIt(toSeq(items(attesting_indices)), it notin custody_bit_1_indices)
|
||||
|
||||
## TODO No fundamental reason to do so many type conversions
|
||||
## verify_indexed_attestation checks for sortedness but it's
|
||||
## entirely a local artifact, seemingly; networking uses the
|
||||
## Attestation data structure, which can't be unsorted. That
|
||||
## the conversion here otherwise needs sorting is due to the
|
||||
## usage of HashSet -- order only matters in one place (that
|
||||
## 0.6.3 highlights and explicates) except in that the spec,
|
||||
## for no obvious reason, verifies it. So, here goes, sort a
|
||||
## list just so a called function can verify it's sorted.
|
||||
IndexedAttestation(
|
||||
custody_bit_0_indices: sorted(
|
||||
mapIt(custody_bit_0_indices, it.uint64), system.cmp),
|
||||
# toSeq pointlessly constructs int-indexable copy so mapIt can infer type;
|
||||
# see above
|
||||
custody_bit_1_indices:
|
||||
sorted(mapIt(toSeq(items(custody_bit_1_indices)), it.uint64),
|
||||
system.cmp),
|
||||
data: attestation.data,
|
||||
signature: attestation.signature,
|
||||
)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#attestations
|
||||
proc checkAttestation*(
|
||||
state: BeaconState, attestation: Attestation, flags: UpdateFlags): 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!
|
||||
|
@ -443,142 +495,45 @@ proc checkAttestation*(
|
|||
if nextSlot in flags: state.slot + 1
|
||||
else: state.slot
|
||||
|
||||
# Can't submit attestations that are too far in history (or in prehistory)
|
||||
if not (attestation.data.slot >= GENESIS_SLOT):
|
||||
warn("Attestation predates genesis slot",
|
||||
attestation_slot = attestation.data.slot,
|
||||
state_slot = humaneSlotNum(stateSlot))
|
||||
return
|
||||
|
||||
if not (stateSlot <= attestation.data.slot + SLOTS_PER_EPOCH):
|
||||
warn("Attestation too old",
|
||||
attestation_slot = humaneSlotNum(attestation.data.slot),
|
||||
state_slot = humaneSlotNum(stateSlot))
|
||||
return
|
||||
|
||||
# Can't submit attestations too quickly
|
||||
if not (
|
||||
attestation.data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= stateSlot):
|
||||
let attestation_slot = get_attestation_slot(state, attestation)
|
||||
if not (attestation_slot + MIN_ATTESTATION_INCLUSION_DELAY <= stateSlot):
|
||||
warn("Attestation too new",
|
||||
attestation_slot = humaneSlotNum(attestation.data.slot),
|
||||
attestation_slot = humaneSlotNum(attestation_slot),
|
||||
state_slot = humaneSlotNum(stateSlot))
|
||||
return
|
||||
|
||||
# # Verify that the justified epoch and root is correct
|
||||
if slot_to_epoch(attestation.data.slot) >= stateSlot.slot_to_epoch():
|
||||
# Case 1: current epoch attestations
|
||||
if not (attestation.data.source_epoch == state.current_justified_epoch):
|
||||
warn("Source epoch is not current justified epoch",
|
||||
attestation_slot = humaneSlotNum(attestation.data.slot),
|
||||
state_slot = humaneSlotNum(stateSlot))
|
||||
return
|
||||
|
||||
if not (attestation.data.source_root == state.current_justified_root):
|
||||
warn("Source root is not current justified root",
|
||||
attestation_slot = humaneSlotNum(attestation.data.slot),
|
||||
state_slot = humaneSlotNum(stateSlot))
|
||||
return
|
||||
else:
|
||||
# Case 2: previous epoch attestations
|
||||
if not (attestation.data.source_epoch == state.previous_justified_epoch):
|
||||
warn("Source epoch is not previous justified epoch",
|
||||
attestation_slot = humaneSlotNum(attestation.data.slot),
|
||||
state_slot = humaneSlotNum(stateSlot))
|
||||
return
|
||||
|
||||
if not (attestation.data.source_root == state.previous_justified_root):
|
||||
warn("Source root is not previous justified root",
|
||||
attestation_slot = humaneSlotNum(attestation.data.slot),
|
||||
state_slot = humaneSlotNum(stateSlot))
|
||||
return
|
||||
|
||||
# Check that the crosslink data is valid
|
||||
let acceptable_crosslink_data = @[
|
||||
# Case 1: Latest crosslink matches the one in the state
|
||||
attestation.data.previous_crosslink,
|
||||
|
||||
# Case 2: State has already been updated, state's latest crosslink matches
|
||||
# the crosslink the attestation is trying to create
|
||||
Crosslink(
|
||||
crosslink_data_root: attestation.data.crosslink_data_root,
|
||||
epoch: slot_to_epoch(attestation.data.slot)
|
||||
)
|
||||
]
|
||||
if not (state.current_crosslinks[attestation.data.shard] in
|
||||
acceptable_crosslink_data):
|
||||
warn("Unexpected crosslink shard",
|
||||
state_latest_crosslinks_attestation_data_shard =
|
||||
state.current_crosslinks[attestation.data.shard],
|
||||
attestation_data_previous_crosslink = attestation.data.previous_crosslink,
|
||||
epoch = humaneEpochNum(slot_to_epoch(attestation.data.slot)),
|
||||
actual_epoch = slot_to_epoch(attestation.data.slot),
|
||||
crosslink_data_root = attestation.data.crosslink_data_root,
|
||||
acceptable_crosslink_data = acceptable_crosslink_data)
|
||||
if not (stateSlot <= attestation_slot + SLOTS_PER_EPOCH):
|
||||
warn("Attestation too old",
|
||||
attestation_slot = humaneSlotNum(attestation_slot),
|
||||
state_slot = humaneSlotNum(stateSlot))
|
||||
return
|
||||
|
||||
# Attestation must be nonempty!
|
||||
if not anyIt(attestation.aggregation_bitfield.bits, it != 0):
|
||||
warn("No signature bits")
|
||||
# Check target epoch, source epoch, source root, and source crosslink
|
||||
let data = attestation.data
|
||||
if not (
|
||||
(data.target_epoch, data.source_epoch, data.source_root, data.previous_crosslink_root) ==
|
||||
(get_current_epoch(state), state.current_justified_epoch,
|
||||
state.current_justified_root,
|
||||
hash_tree_root(state.current_crosslinks[data.shard])) or
|
||||
(data.target_epoch, data.source_epoch, data.source_root, data.previous_crosslink_root) ==
|
||||
(get_previous_epoch(state), state.previous_justified_epoch,
|
||||
state.previous_justified_root,
|
||||
hash_tree_root(state.previous_crosslinks[data.shard]))):
|
||||
warn("checkAttestation: target epoch, source epoch, source root, or source crosslink invalid")
|
||||
return
|
||||
|
||||
# Custody must be empty (to be removed in phase 1)
|
||||
if not allIt(attestation.custody_bitfield.bits, it == 0):
|
||||
warn("Custody bits set in phase0")
|
||||
return
|
||||
|
||||
# Get the committee for the specific shard that this attestation is for
|
||||
let crosslink_committee = mapIt(
|
||||
filterIt(get_crosslink_committees_at_slot(state, attestation.data.slot),
|
||||
it.shard == attestation.data.shard),
|
||||
it.committee)[0]
|
||||
|
||||
# Custody bitfield must be a subset of the attestation bitfield
|
||||
if not allIt(0 ..< len(crosslink_committee),
|
||||
if not get_bitfield_bit(attestation.aggregation_bitfield, it):
|
||||
not get_bitfield_bit(attestation.custody_bitfield, it)
|
||||
else:
|
||||
true):
|
||||
warn("Wrong custody bits set")
|
||||
return
|
||||
|
||||
# Verify aggregate signature
|
||||
let
|
||||
participants = get_attestation_participants(
|
||||
state, attestation.data, attestation.aggregation_bitfield)
|
||||
|
||||
## TODO when the custody_bitfield assertion-to-emptiness disappears do this
|
||||
## and fix the custody_bit_0_participants check to depend on it.
|
||||
# custody_bit_1_participants = {nothing, always, because assertion above}
|
||||
custody_bit_1_participants: seq[ValidatorIndex] = @[]
|
||||
custody_bit_0_participants = participants
|
||||
|
||||
if skipValidation notin flags:
|
||||
# Verify that aggregate_signature verifies using the group pubkey.
|
||||
if not bls_verify_multiple(
|
||||
@[
|
||||
bls_aggregate_pubkeys(mapIt(custody_bit_0_participants,
|
||||
state.validator_registry[it].pubkey)),
|
||||
bls_aggregate_pubkeys(mapIt(custody_bit_1_participants,
|
||||
state.validator_registry[it].pubkey)),
|
||||
],
|
||||
@[
|
||||
hash_tree_root(AttestationDataAndCustodyBit(
|
||||
data: attestation.data, custody_bit: false)),
|
||||
hash_tree_root(AttestationDataAndCustodyBit(
|
||||
data: attestation.data, custody_bit: true)),
|
||||
],
|
||||
attestation.aggregate_signature,
|
||||
get_domain(state, DOMAIN_ATTESTATION,
|
||||
slot_to_epoch(attestation.data.slot))
|
||||
):
|
||||
warn("Invalid attestation signature")
|
||||
return
|
||||
|
||||
# Crosslink data root is zero (to be removed in phase 1)
|
||||
## Check crosslink data root
|
||||
## [to be removed in phase 1]
|
||||
if attestation.data.crosslink_data_root != ZERO_HASH:
|
||||
warn("Invalid crosslink data root")
|
||||
return
|
||||
|
||||
# Check signature and bitfields
|
||||
if not verify_indexed_attestation(
|
||||
state, convert_to_indexed(state, attestation)):
|
||||
warn("checkAttestation: signature or bitfields incorrect")
|
||||
return
|
||||
|
||||
true
|
||||
|
||||
proc makeAttestationData*(
|
||||
|
@ -600,8 +555,8 @@ proc makeAttestationData*(
|
|||
beacon_block_root: beacon_block_root,
|
||||
target_root: target_root,
|
||||
crosslink_data_root: Eth2Digest(), # Stub in phase0
|
||||
previous_crosslink: state.current_crosslinks[shard],
|
||||
previous_crosslink_root: hash_tree_root(state.current_crosslinks[shard]),
|
||||
source_epoch: state.current_justified_epoch,
|
||||
source_root: state.current_justified_root,
|
||||
target_epoch: slot_to_epoch(epoch_start_slot)
|
||||
target_epoch: slot_to_epoch(state.slot)
|
||||
)
|
||||
|
|
|
@ -57,21 +57,15 @@ const
|
|||
## Spec version we're aiming to be compatible with, right now
|
||||
## TODO: improve this scheme once we can negotiate versions in protocol
|
||||
|
||||
# Gwei values
|
||||
# ---------------------------------------------------------------
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#gwei-values
|
||||
|
||||
# TODO remove erstwhile blob/v0.6.3
|
||||
FORK_CHOICE_BALANCE_INCREMENT* = 2'u64^0 * 10'u64^9
|
||||
|
||||
# Initial values
|
||||
# ---------------------------------------------------------------
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#initial-values
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#initial-values
|
||||
GENESIS_EPOCH* = (GENESIS_SLOT.uint64 div SLOTS_PER_EPOCH).Epoch ##\
|
||||
## slot_to_epoch(GENESIS_SLOT)
|
||||
GENESIS_START_SHARD* = 0'u64
|
||||
ZERO_HASH* = Eth2Digest()
|
||||
EMPTY_SIGNATURE* = ValidatorSig()
|
||||
|
||||
type
|
||||
ValidatorIndex* = range[0'u32 .. 0xFFFFFF'u32] # TODO: wrap-around
|
||||
|
@ -97,18 +91,16 @@ type
|
|||
attestation_2*: IndexedAttestation ## \
|
||||
## Second attestation
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#slashableattestation
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#indexedattestation
|
||||
IndexedAttestation* = object
|
||||
validator_indices*: seq[uint64] ##\
|
||||
## Validator indices
|
||||
# These probably should be seq[ValidatorIndex], but that throws RLP errors
|
||||
custody_bit_0_indices*: seq[uint64]
|
||||
custody_bit_1_indices*: seq[uint64]
|
||||
|
||||
data*: AttestationData ## \
|
||||
## Attestation data
|
||||
|
||||
custody_bitfield*: BitField ##\
|
||||
## Custody bitfield
|
||||
|
||||
aggregate_signature*: ValidatorSig ## \
|
||||
signature*: ValidatorSig ## \
|
||||
## Aggregate signature
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#attestation
|
||||
|
@ -122,13 +114,13 @@ type
|
|||
custody_bitfield*: BitField ##\
|
||||
## Custody bitfield
|
||||
|
||||
aggregate_signature*: ValidatorSig ##\
|
||||
signature*: ValidatorSig ##\
|
||||
## BLS aggregate signature
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#attestationdata
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.2/specs/core/0_beacon-chain.md#attestationdata
|
||||
AttestationData* = object
|
||||
slot*: Slot # TODO remove this, once figure out attestation pool issues
|
||||
# LMD GHOST vote
|
||||
slot*: Slot
|
||||
beacon_block_root*: Eth2Digest
|
||||
|
||||
# FFG vote
|
||||
|
@ -139,7 +131,7 @@ type
|
|||
|
||||
# Crosslink vote
|
||||
shard*: uint64
|
||||
previous_crosslink*: Crosslink
|
||||
previous_crosslink_root*: Eth2Digest
|
||||
crosslink_data_root*: Eth2Digest
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#attestationdataandcustodybit
|
||||
|
@ -460,17 +452,12 @@ func shortLog*(v: BeaconBlock): tuple[
|
|||
shortLog(v.signature)
|
||||
)
|
||||
|
||||
func shortLog*(v: AttestationData): tuple[
|
||||
slot: uint64, beacon_block_root: string, source_epoch: uint64,
|
||||
target_root: string, source_root: string, shard: uint64,
|
||||
previous_crosslink_epoch: uint64, previous_crosslink_data_root: string,
|
||||
crosslink_data_root: string
|
||||
] = (
|
||||
humaneSlotNum(v.slot), shortLog(v.beacon_block_root),
|
||||
func shortLog*(v: AttestationData): auto =
|
||||
(
|
||||
shortLog(v.beacon_block_root),
|
||||
humaneEpochNum(v.source_epoch), shortLog(v.target_root),
|
||||
shortLog(v.source_root),
|
||||
v.shard, humaneEpochNum(v.previous_crosslink.epoch),
|
||||
shortLog(v.previous_crosslink.crosslink_data_root),
|
||||
v.shard, v.previous_crosslink_root,
|
||||
shortLog(v.crosslink_data_root)
|
||||
)
|
||||
|
||||
|
|
|
@ -81,7 +81,7 @@ func get_active_validator_indices*(state: BeaconState, epoch: Epoch):
|
|||
if is_active_validator(val, epoch):
|
||||
result.add idx.ValidatorIndex
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#get_epoch_committee_count
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#get_epoch_committee_count
|
||||
func get_epoch_committee_count*(state: BeaconState, epoch: Epoch): uint64 =
|
||||
# Return the number of committees at ``epoch``.
|
||||
let active_validator_indices = get_active_validator_indices(state, epoch)
|
||||
|
@ -95,13 +95,12 @@ func get_current_epoch*(state: BeaconState): Epoch =
|
|||
doAssert state.slot >= GENESIS_SLOT, $state.slot
|
||||
slot_to_epoch(state.slot)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#get_randao_mix
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#get_randao_mix
|
||||
func get_randao_mix*(state: BeaconState,
|
||||
epoch: Epoch): Eth2Digest =
|
||||
## Returns the randao mix at a recent ``epoch``.
|
||||
## ``epoch`` expected to be between
|
||||
## (current_epoch - LATEST_ACTIVE_INDEX_ROOTS_LENGTH + ACTIVATION_EXIT_DELAY,
|
||||
## current_epoch + ACTIVATION_EXIT_DELAY].
|
||||
## ``epoch`` expected to be between (current_epoch -
|
||||
## LATEST_RANDAO_MIXES_LENGTH, current_epoch].
|
||||
state.latest_randao_mixes[epoch mod LATEST_RANDAO_MIXES_LENGTH]
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#get_active_index_root
|
||||
|
@ -152,7 +151,7 @@ func int_to_bytes4*(x: uint64): array[4, byte] =
|
|||
result[2] = ((x shr 16) and 0xff).byte
|
||||
result[3] = ((x shr 24) and 0xff).byte
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.0/specs/core/0_beacon-chain.md#get_domain
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#get_domain
|
||||
func get_domain*(
|
||||
state: BeaconState, domain_type: SignatureDomain, message_epoch: Epoch): uint64 =
|
||||
## Return the signature domain (fork version concatenated with domain type)
|
||||
|
|
|
@ -22,7 +22,7 @@ type
|
|||
|
||||
{.experimental: "codeReordering".} # SLOTS_PER_EPOCH is use before being defined in spec
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/configs/constant_presets/mainnet.yaml
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/configs/constant_presets/mainnet.yaml
|
||||
const
|
||||
# Misc
|
||||
# ---------------------------------------------------------------
|
||||
|
@ -83,16 +83,16 @@ const
|
|||
|
||||
# Initial values
|
||||
# ---------------------------------------------------------------
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#initial-values
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/configs/constant_presets/mainnet.yaml#L44
|
||||
|
||||
GENESIS_FORK_VERSION* = [0'u8, 0'u8, 0'u8, 0'u8]
|
||||
GENESIS_SLOT* = 64.Slot
|
||||
GENESIS_SLOT* = 0.Slot
|
||||
FAR_FUTURE_EPOCH* = (not 0'u64).Epoch # 2^64 - 1 in spec
|
||||
BLS_WITHDRAWAL_PREFIX_BYTE* = 0'u8
|
||||
|
||||
# Time parameters
|
||||
# ---------------------------------------------------------------
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_fork-choice.md#time-parameters
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_fork-choice.md#time-parameters
|
||||
|
||||
SECONDS_PER_SLOT*{.intdefine.} = 6'u64 # Compile with -d:SECONDS_PER_SLOT=1 for 6x faster slots
|
||||
## TODO consistent time unit across projects, similar to C++ chrono?
|
||||
|
|
|
@ -26,7 +26,7 @@ type
|
|||
const
|
||||
# Misc
|
||||
# ---------------------------------------------------------------
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#misc
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#misc
|
||||
|
||||
# Changed
|
||||
SHARD_COUNT* {.intdefine.} = 8
|
||||
|
@ -61,11 +61,11 @@ const
|
|||
|
||||
# Initial values
|
||||
# ---------------------------------------------------------------
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#initial-values
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/configs/constant_presets/minimal.yaml#L43
|
||||
|
||||
# Unchanged
|
||||
GENESIS_FORK_VERSION* = [0'u8, 0'u8, 0'u8, 0'u8]
|
||||
GENESIS_SLOT* = 64.Slot
|
||||
GENESIS_SLOT* = 0.Slot
|
||||
FAR_FUTURE_EPOCH* = (not 0'u64).Epoch # 2^64 - 1 in spec
|
||||
BLS_WITHDRAWAL_PREFIX_BYTE* = 0'u8
|
||||
|
||||
|
@ -89,7 +89,7 @@ const
|
|||
|
||||
# Changed
|
||||
SLOTS_PER_ETH1_VOTING_PERIOD* = 16
|
||||
SLOTS_PER_HISTORICAL_ROOT* = 64
|
||||
SLOTS_PER_HISTORICAL_ROOT* = 128 # 64 doesn't work with GENESIS_SLOT == 0?
|
||||
|
||||
# Unchanged
|
||||
MIN_VALIDATOR_WITHDRAWABILITY_DELAY* = 2'u64^8
|
||||
|
|
|
@ -13,8 +13,8 @@ import
|
|||
./crypto, ./datatypes, ./digest, ./helpers
|
||||
|
||||
# TODO: Proceed to renaming and signature changes
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#get_shuffled_index
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#compute_committee
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#get_shuffled_index
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#compute_committee
|
||||
func get_shuffled_seq*(seed: Eth2Digest,
|
||||
list_size: uint64,
|
||||
): seq[ValidatorIndex] =
|
||||
|
@ -118,7 +118,7 @@ func get_shuffled_index(index: ValidatorIndex, index_count: uint64, seed: Eth2Di
|
|||
if bit != 0:
|
||||
result = flip
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#get_previous_epoch
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#get_previous_epoch
|
||||
func get_previous_epoch*(state: BeaconState): Epoch =
|
||||
## Return the previous epoch of the given ``state``.
|
||||
## Return the current epoch if it's genesis epoch.
|
||||
|
@ -149,7 +149,7 @@ func get_epoch_start_shard*(state: BeaconState, epoch: Epoch): Shard =
|
|||
SHARD_COUNT
|
||||
return shard
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#compute_committee
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#compute_committee
|
||||
func compute_committee(indices: seq[ValidatorIndex], seed: Eth2Digest,
|
||||
index: uint64, count: uint64): seq[ValidatorIndex] =
|
||||
let
|
||||
|
@ -212,7 +212,7 @@ iterator get_crosslink_committees_at_slot_cached*(
|
|||
cache.crosslink_committee_cache[key] = result
|
||||
for v in result: yield v
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#get_beacon_proposer_index
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#get_beacon_proposer_index
|
||||
func get_beacon_proposer_index*(state: BeaconState): ValidatorIndex =
|
||||
# Return the current beacon proposer index.
|
||||
const
|
||||
|
|
|
@ -171,56 +171,6 @@ proc processProposerSlashings(
|
|||
|
||||
true
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.0/specs/core/0_beacon-chain.md#verify_slashable_attestation
|
||||
func verify_slashable_attestation(state: BeaconState, slashable_attestation: IndexedAttestation): bool =
|
||||
# Verify validity of ``slashable_attestation`` fields.
|
||||
|
||||
if anyIt(slashable_attestation.custody_bitfield.bits, it != 0): # [TO BE REMOVED IN PHASE 1]
|
||||
return false
|
||||
|
||||
if len(slashable_attestation.validator_indices) == 0:
|
||||
return false
|
||||
|
||||
for i in 0 ..< (len(slashable_attestation.validator_indices) - 1):
|
||||
if slashable_attestation.validator_indices[i] >= slashable_attestation.validator_indices[i + 1]:
|
||||
return false
|
||||
|
||||
if not verify_bitfield(slashable_attestation.custody_bitfield,
|
||||
len(slashable_attestation.validator_indices)):
|
||||
return false
|
||||
|
||||
if len(slashable_attestation.validator_indices) > MAX_INDICES_PER_ATTESTATION:
|
||||
return false
|
||||
|
||||
var
|
||||
custody_bit_0_indices: seq[uint64] = @[]
|
||||
custody_bit_1_indices: seq[uint64] = @[]
|
||||
|
||||
for i, validator_index in slashable_attestation.validator_indices:
|
||||
if not get_bitfield_bit(slashable_attestation.custody_bitfield, i):
|
||||
custody_bit_0_indices.add(validator_index)
|
||||
else:
|
||||
custody_bit_1_indices.add(validator_index)
|
||||
|
||||
bls_verify_multiple(
|
||||
@[
|
||||
bls_aggregate_pubkeys(mapIt(custody_bit_0_indices, state.validator_registry[it.int].pubkey)),
|
||||
bls_aggregate_pubkeys(mapIt(custody_bit_1_indices, state.validator_registry[it.int].pubkey)),
|
||||
],
|
||||
@[
|
||||
hash_tree_root(AttestationDataAndCustodyBit(
|
||||
data: slashable_attestation.data, custody_bit: false)),
|
||||
hash_tree_root(AttestationDataAndCustodyBit(
|
||||
data: slashable_attestation.data, custody_bit: true)),
|
||||
],
|
||||
slashable_attestation.aggregate_signature,
|
||||
get_domain(
|
||||
state,
|
||||
DOMAIN_ATTESTATION,
|
||||
slot_to_epoch(slashable_attestation.data.slot),
|
||||
),
|
||||
)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#is_slashable_attestation_data
|
||||
func is_slashable_attestation_data(
|
||||
data_1: AttestationData, data_2: AttestationData): bool =
|
||||
|
@ -233,13 +183,14 @@ func is_slashable_attestation_data(
|
|||
(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.5.0/specs/core/0_beacon-chain.md#attester-slashings
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#attester-slashings
|
||||
proc processAttesterSlashings(state: var BeaconState, blck: BeaconBlock): bool =
|
||||
# Process ``AttesterSlashing`` operation.
|
||||
if len(blck.body.attester_slashings) > MAX_ATTESTER_SLASHINGS:
|
||||
notice "CaspSlash: too many!"
|
||||
return false
|
||||
|
||||
result = true
|
||||
for attester_slashing in blck.body.attester_slashings:
|
||||
let
|
||||
attestation_1 = attester_slashing.attestation_1
|
||||
|
@ -250,32 +201,32 @@ proc processAttesterSlashings(state: var BeaconState, blck: BeaconBlock): bool =
|
|||
notice "CaspSlash: surround or double vote check failed"
|
||||
return false
|
||||
|
||||
if not verify_slashable_attestation(state, attestation_1):
|
||||
if not verify_indexed_attestation(state, attestation_1):
|
||||
notice "CaspSlash: invalid votes 1"
|
||||
return false
|
||||
|
||||
if not verify_slashable_attestation(state, attestation_2):
|
||||
if not verify_indexed_attestation(state, attestation_2):
|
||||
notice "CaspSlash: invalid votes 2"
|
||||
return false
|
||||
|
||||
var slashed_any = false
|
||||
|
||||
let
|
||||
indices2 = toSet(attestation_2.validator_indices)
|
||||
slashable_indices =
|
||||
attestation_1.validator_indices.filterIt(
|
||||
it in indices2 and not state.validator_registry[it.int].slashed)
|
||||
## TODO there's a lot of sorting/set construction here and
|
||||
## verify_indexed_attestation, but go by spec unless there
|
||||
## is compelling perf evidence otherwise.
|
||||
let attesting_indices_1 =
|
||||
attestation_1.custody_bit_0_indices & attestation_1.custody_bit_1_indices
|
||||
let attesting_indices_2 =
|
||||
attestation_2.custody_bit_0_indices & attestation_2.custody_bit_1_indices
|
||||
for index in sorted(toSeq(intersection(toSet(attesting_indices_1),
|
||||
toSet(attesting_indices_2)).items), system.cmp):
|
||||
if is_slashable_validator(state.validator_registry[index.int],
|
||||
get_current_epoch(state)):
|
||||
slash_validator(state, index.ValidatorIndex)
|
||||
slashed_any = true
|
||||
result = result and slashed_any
|
||||
|
||||
if not (len(slashable_indices) >= 1):
|
||||
notice "CaspSlash: no intersection"
|
||||
return false
|
||||
|
||||
for index in slashable_indices:
|
||||
slash_validator(state, index.ValidatorIndex)
|
||||
|
||||
true
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.0/specs/core/0_beacon-chain.md#attestations
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#attestations
|
||||
proc processAttestations(
|
||||
state: var BeaconState, blck: BeaconBlock, flags: UpdateFlags): bool =
|
||||
## Each block includes a number of attestations that the proposer chose. Each
|
||||
|
@ -296,6 +247,7 @@ proc processAttestations(
|
|||
var committee_count_cache = initTable[Epoch, uint64]()
|
||||
|
||||
for attestation in blck.body.attestations:
|
||||
# Caching
|
||||
let
|
||||
epoch = attestation.data.target_epoch
|
||||
committee_count = if epoch in committee_count_cache:
|
||||
|
@ -303,6 +255,8 @@ proc processAttestations(
|
|||
else:
|
||||
get_epoch_committee_count(state, epoch)
|
||||
committee_count_cache[epoch] = committee_count
|
||||
|
||||
# Spec content
|
||||
let attestation_slot =
|
||||
get_attestation_slot(state, attestation, committee_count)
|
||||
let pending_attestation = PendingAttestation(
|
||||
|
@ -312,7 +266,7 @@ proc processAttestations(
|
|||
proposer_index: get_beacon_proposer_index(state),
|
||||
)
|
||||
|
||||
if slot_to_epoch(attestation.data.slot) == 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)
|
||||
|
@ -520,6 +474,20 @@ proc processBlock(
|
|||
|
||||
true
|
||||
|
||||
# TODO this cached version corresponds to the blob/v0.5.1ish get_attesting_indices
|
||||
# rm/make consistent with 0.6 version above
|
||||
func get_attesting_indices_cached(
|
||||
state: BeaconState,
|
||||
attestations: openArray[PendingAttestation], cache: var StateCache):
|
||||
HashSet[ValidatorIndex] =
|
||||
# Union of attesters that participated in some attestations
|
||||
result = initSet[ValidatorIndex]()
|
||||
for attestation in attestations:
|
||||
for validator_index in get_attestation_participants_cached(
|
||||
state, attestation.data, attestation.aggregation_bitfield,
|
||||
cache):
|
||||
result.incl validator_index
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#helper-functions-1
|
||||
func get_total_active_balance(state: BeaconState): Gwei =
|
||||
return get_total_balance(
|
||||
|
@ -549,38 +517,6 @@ func get_matching_head_attestations(state: BeaconState, epoch: Epoch):
|
|||
get_block_root_at_slot(state, get_attestation_slot(state, it))
|
||||
)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#get_attesting_indices
|
||||
func get_attesting_indices(state: BeaconState,
|
||||
attestation_data: AttestationData,
|
||||
bitfield: BitField): HashSet[ValidatorIndex] =
|
||||
## Return the sorted attesting indices corresponding to ``attestation_data``
|
||||
## and ``bitfield``.
|
||||
## The spec goes through a lot of hoops to sort things, and sometimes
|
||||
## constructs sets from the results here. The basic idea is to always
|
||||
## just do the right thing and keep it in a HashSet.
|
||||
result = initSet[ValidatorIndex]()
|
||||
let committee =
|
||||
get_crosslink_committee(state, attestation_data.target_epoch,
|
||||
attestation_data.shard)
|
||||
doAssert verify_bitfield(bitfield, len(committee))
|
||||
for i, index in committee:
|
||||
if get_bitfield_bit(bitfield, i):
|
||||
result.incl index
|
||||
|
||||
# TODO this cached version corresponds to the blob/v0.5.1ish get_attesting_indices
|
||||
# rm/make consistent with 0.6 version above
|
||||
func get_attesting_indices_cached(
|
||||
state: BeaconState,
|
||||
attestations: openArray[PendingAttestation], cache: var StateCache):
|
||||
HashSet[ValidatorIndex] =
|
||||
# Union of attesters that participated in some attestations
|
||||
result = initSet[ValidatorIndex]()
|
||||
for attestation in attestations:
|
||||
for validator_index in get_attestation_participants_cached(
|
||||
state, attestation.data, attestation.aggregation_bitfield,
|
||||
cache):
|
||||
result.incl validator_index
|
||||
|
||||
func get_unslashed_attesting_indices(
|
||||
state: BeaconState, attestations: seq[PendingAttestation]):
|
||||
HashSet[ValidatorIndex] =
|
||||
|
@ -593,8 +529,8 @@ func get_unslashed_attesting_indices(
|
|||
if state.validator_registry[index].slashed:
|
||||
result.excl index
|
||||
|
||||
func get_attesting_balance(state: BeaconState,
|
||||
attestations: seq[PendingAttestation]): Gwei =
|
||||
func get_attesting_balance(
|
||||
state: BeaconState, attestations: seq[PendingAttestation]): Gwei =
|
||||
get_total_balance(state, get_unslashed_attesting_indices(state, attestations))
|
||||
|
||||
func get_attesting_balance_cached(
|
||||
|
@ -603,7 +539,16 @@ func get_attesting_balance_cached(
|
|||
get_total_balance(state, get_attesting_indices_cached(
|
||||
state, attestations, cache))
|
||||
|
||||
# Not exactly in spec, but for get_winning_root_and_participants
|
||||
func get_crosslink_from_attestation_data(
|
||||
state: BeaconState, data: AttestationData): Crosslink =
|
||||
Crosslink(
|
||||
epoch: min(data.target_epoch,
|
||||
state.current_crosslinks[data.shard].epoch + MAX_CROSSLINK_EPOCHS),
|
||||
previous_crosslink_root: data.previous_crosslink_root,
|
||||
crosslink_data_root: data.crosslink_data_root,
|
||||
)
|
||||
|
||||
# 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
|
||||
|
@ -611,52 +556,65 @@ func lowerThan(candidate, current: Eth2Digest): bool =
|
|||
if v > candidate.data[i]: return true
|
||||
false
|
||||
|
||||
func get_winning_root_and_participants(
|
||||
state: BeaconState, shard: Shard, cache: var StateCache):
|
||||
tuple[a: Eth2Digest, b: HashSet[ValidatorIndex]] =
|
||||
# TODO check/profile if should add cache: var StateCache param
|
||||
func get_winning_crosslink_and_attesting_indices(
|
||||
state: BeaconState, epoch: Epoch, shard: Shard): tuple[a: Crosslink, b: HashSet[ValidatorIndex]] =
|
||||
let
|
||||
all_attestations =
|
||||
concat(state.current_epoch_attestations,
|
||||
state.previous_epoch_attestations)
|
||||
valid_attestations =
|
||||
## TODO Z-F could help here
|
||||
## TODO get_winning_crosslink_and_attesting_indices was profiling hotspot
|
||||
shard_attestations =
|
||||
filterIt(
|
||||
all_attestations,
|
||||
it.data.previous_crosslink == state.current_crosslinks[shard])
|
||||
all_roots = mapIt(valid_attestations, it.data.crosslink_data_root)
|
||||
get_matching_source_attestations(state, epoch), it.data.shard == shard)
|
||||
shard_crosslinks =
|
||||
mapIt(shard_attestations,
|
||||
get_crosslink_from_attestation_data(state, it.data))
|
||||
# TODO this seems like a lot of hash_tree_root'ing on same data
|
||||
candidate_crosslinks =
|
||||
filterIt(shard_crosslinks,
|
||||
hash_tree_root(state.current_crosslinks[shard]) in
|
||||
# TODO pointless memory allocation, etc.
|
||||
@[it.previous_crosslink_root, hash_tree_root(it)])
|
||||
|
||||
# handle when no attestations for shard available
|
||||
if len(all_roots) == 0:
|
||||
return (ZERO_HASH, initSet[ValidatorIndex]())
|
||||
if len(candidate_crosslinks) == 0:
|
||||
return (Crosslink(), initSet[ValidatorIndex]())
|
||||
|
||||
# 0.5.1 spec has less-than-ideal get_attestations_for nested function.
|
||||
var attestations_for = initTable[Eth2Digest, seq[PendingAttestation]]()
|
||||
for valid_attestation in valid_attestations:
|
||||
if valid_attestation.data.crosslink_data_root in attestations_for:
|
||||
attestations_for[valid_attestation.data.crosslink_data_root].add(
|
||||
valid_attestation)
|
||||
else:
|
||||
attestations_for[valid_attestation.data.crosslink_data_root] =
|
||||
@[valid_attestation]
|
||||
## TODO check if should cache this again, as with 0.5
|
||||
## var attestations_for = initTable[Eth2Digest, seq[PendingAttestation]]()
|
||||
## for valid_attestation in valid_attestations:
|
||||
## if valid_attestation.data.crosslink_data_root in attestations_for:
|
||||
## attestations_for[valid_attestation.data.crosslink_data_root].add(
|
||||
## valid_attestation)
|
||||
## else:
|
||||
## attestations_for[valid_attestation.data.crosslink_data_root] =
|
||||
## @[valid_attestation]
|
||||
## TODO either way, this nested function not great; {.fastcall.} pragma
|
||||
## not directly applicable either, since it does need some closure
|
||||
func get_attestations_for(crosslink: Crosslink): seq[PendingAttestation] =
|
||||
filterIt(shard_attestations,
|
||||
get_crosslink_from_attestation_data(state, it.data) == crosslink)
|
||||
|
||||
## Winning crosslink root is the root with the most votes for it, ties broken
|
||||
## in favor of lexicographically higher hash
|
||||
## Winning crosslink has the crosslink data root with the most balance voting
|
||||
## for it (ties broken lexicographically)
|
||||
var
|
||||
winning_root: Eth2Digest
|
||||
winning_root_balance = 0'u64
|
||||
winning_crosslink: Crosslink
|
||||
winning_crosslink_balance = 0.Gwei
|
||||
|
||||
for r in all_roots:
|
||||
let root_balance = get_attesting_balance_cached(
|
||||
state, attestations_for.getOrDefault(r), cache)
|
||||
if (root_balance > winning_root_balance or
|
||||
(root_balance == winning_root_balance and
|
||||
lowerThan(winning_root, r))):
|
||||
winning_root = r
|
||||
winning_root_balance = root_balance
|
||||
for candidate_crosslink in candidate_crosslinks:
|
||||
## TODO check if should cache this again
|
||||
## let root_balance = get_attesting_balance_cached(
|
||||
## state, attestations_for.getOrDefault(r), cache)
|
||||
let crosslink_balance =
|
||||
get_attesting_balance(state, get_attestations_for(candidate_crosslink))
|
||||
if (crosslink_balance > winning_crosslink_balance or
|
||||
(winning_crosslink_balance == crosslink_balance and
|
||||
lowerThan(winning_crosslink.crosslink_data_root,
|
||||
candidate_crosslink.crosslink_data_root))):
|
||||
winning_crosslink = candidate_crosslink
|
||||
winning_crosslink_balance = crosslink_balance
|
||||
|
||||
(winning_root,
|
||||
get_attesting_indices_cached(
|
||||
state,
|
||||
attestations_for.getOrDefault(winning_root), cache))
|
||||
(winning_crosslink,
|
||||
get_unslashed_attesting_indices(state,
|
||||
get_attestations_for(winning_crosslink)))
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#justification-and-finalization
|
||||
func process_justification_and_finalization(state: var BeaconState) =
|
||||
|
@ -723,48 +681,32 @@ func process_justification_and_finalization(state: var BeaconState) =
|
|||
state.finalized_epoch = old_current_justified_epoch
|
||||
state.finalized_root = get_block_root(state, state.finalized_epoch)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.5.1/specs/core/0_beacon-chain.md#crosslinks
|
||||
func process_crosslinks(
|
||||
state: var BeaconState, per_epoch_cache: var StateCache) =
|
||||
let
|
||||
current_epoch = get_current_epoch(state)
|
||||
previous_epoch = current_epoch - 1
|
||||
next_epoch = current_epoch + 1
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#crosslinks
|
||||
func process_crosslinks(state: var BeaconState, per_epoch_cache: var StateCache) =
|
||||
## TODO is there a semantic reason for this, or is this just a way to force
|
||||
## copying? If so, why not just `list(foo)` or similar? This is strange. In
|
||||
## this case, for type reasons, don't do weird
|
||||
## [c for c in state.current_crosslinks] from spec.
|
||||
state.previous_crosslinks = state.current_crosslinks
|
||||
|
||||
## TODO is it actually correct to be setting state.current_crosslinks[shard]
|
||||
## to something pre-GENESIS_EPOCH, ever? I guess the intent is if there are
|
||||
## a quorum of participants for get_epoch_start_slot(previous_epoch), when
|
||||
## state.slot == GENESIS_SLOT, then there will be participants for a quorum
|
||||
## in the current-epoch (i.e. genesis epoch) version of that shard?
|
||||
#for slot in get_epoch_start_slot(previous_epoch).uint64 ..<
|
||||
for slot in max(
|
||||
GENESIS_SLOT.uint64, get_epoch_start_slot(previous_epoch).uint64) ..<
|
||||
get_epoch_start_slot(next_epoch).uint64:
|
||||
for cas in get_crosslink_committees_at_slot_cached(
|
||||
state, slot, per_epoch_cache):
|
||||
for epoch_int in get_previous_epoch(state).uint64 ..
|
||||
get_current_epoch(state).uint64:
|
||||
# This issue comes up regularly -- iterating means an int type,
|
||||
# which then needs re-conversion back to specialized type.
|
||||
let epoch = epoch_int.Epoch
|
||||
for offset in 0'u64 ..< get_epoch_committee_count(state, epoch):
|
||||
let
|
||||
(crosslink_committee, shard) = cas
|
||||
shard = (get_epoch_start_shard(state, epoch) + offset) mod SHARD_COUNT
|
||||
crosslink_committee = get_crosslink_committee(state, epoch, shard)
|
||||
# In general, it'll loop over the same shards twice, and
|
||||
# get_winning_root_and_participants is defined to return
|
||||
# the same results from the previous epoch as current.
|
||||
(winning_root, participants) =
|
||||
if shard notin per_epoch_cache.winning_root_participants_cache:
|
||||
get_winning_root_and_participants(state, shard, per_epoch_cache)
|
||||
else:
|
||||
(ZERO_HASH, per_epoch_cache.winning_root_participants_cache[shard])
|
||||
participating_balance = get_total_balance(state, participants)
|
||||
total_balance = get_total_balance(state, crosslink_committee)
|
||||
|
||||
per_epoch_cache.winning_root_participants_cache[shard] = participants
|
||||
|
||||
if 3'u64 * participating_balance >= 2'u64 * total_balance:
|
||||
# Check not from spec; seems kludgy
|
||||
doAssert slot >= GENESIS_SLOT
|
||||
|
||||
state.current_crosslinks[shard] = Crosslink(
|
||||
epoch: slot_to_epoch(slot),
|
||||
crosslink_data_root: winning_root
|
||||
)
|
||||
# TODO cache like before, in 0.5 version of this function
|
||||
(winning_crosslink, attesting_indices) =
|
||||
get_winning_crosslink_and_attesting_indices(state, epoch, shard)
|
||||
if 3'u64 * get_total_balance(state, attesting_indices) >=
|
||||
2'u64 * get_total_balance(state, crosslink_committee):
|
||||
state.current_crosslinks[shard] = winning_crosslink
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#rewards-and-penalties
|
||||
func get_base_reward(state: BeaconState, index: ValidatorIndex): Gwei =
|
||||
|
@ -775,7 +717,7 @@ func get_base_reward(state: BeaconState, index: ValidatorIndex): Gwei =
|
|||
state.validator_registry[index].effective_balance div adjusted_quotient div
|
||||
BASE_REWARDS_PER_EPOCH
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#rewards-and-penalties
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#rewards-and-penalties
|
||||
func get_attestation_deltas(state: BeaconState):
|
||||
tuple[a: seq[Gwei], b: seq[Gwei]] =
|
||||
let
|
||||
|
@ -846,40 +788,34 @@ func get_attestation_deltas(state: BeaconState):
|
|||
|
||||
(rewards, penalties)
|
||||
|
||||
# blob/0.5.1
|
||||
# TODO re-cache this one, as under 0.5 version, if profiling suggests it
|
||||
func get_crosslink_deltas(state: BeaconState, cache: var StateCache):
|
||||
tuple[a: seq[Gwei], b: seq[Gwei]] =
|
||||
|
||||
var
|
||||
rewards = repeat(0'u64, len(state.validator_registry))
|
||||
penalties = repeat(0'u64, len(state.validator_registry))
|
||||
let
|
||||
previous_epoch_start_slot =
|
||||
get_epoch_start_slot(get_previous_epoch(state))
|
||||
current_epoch_start_slot =
|
||||
get_epoch_start_slot(get_current_epoch(state))
|
||||
for slot in previous_epoch_start_slot.uint64 ..<
|
||||
current_epoch_start_slot.uint64:
|
||||
for cas in get_crosslink_committees_at_slot_cached(state, slot, cache):
|
||||
let
|
||||
(crosslink_committee, shard) = cas
|
||||
(winning_root, participants) =
|
||||
if shard notin cache.winning_root_participants_cache:
|
||||
get_winning_root_and_participants(state, shard, cache)
|
||||
else:
|
||||
(ZERO_HASH, cache.winning_root_participants_cache[shard])
|
||||
participating_balance = get_total_balance(state, participants)
|
||||
total_balance = get_total_balance(state, crosslink_committee)
|
||||
for index in crosslink_committee:
|
||||
if index in participants:
|
||||
rewards[index] +=
|
||||
get_base_reward(state, index) * participating_balance div
|
||||
total_balance
|
||||
else:
|
||||
penalties[index] += get_base_reward(state, index)
|
||||
let epoch = get_previous_epoch(state)
|
||||
for offset in 0'u64 ..< get_epoch_committee_count(state, epoch):
|
||||
let
|
||||
shard = (get_epoch_start_shard(state, epoch) + offset) mod SHARD_COUNT
|
||||
crosslink_committee = get_crosslink_committee(state, epoch, shard)
|
||||
(winning_crosslink, attesting_indices) =
|
||||
get_winning_crosslink_and_attesting_indices(state, epoch, shard)
|
||||
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] +=
|
||||
get_base_reward(state, index) * attesting_balance div
|
||||
committee_balance
|
||||
else:
|
||||
penalties[index] += get_base_reward(state, index)
|
||||
|
||||
(rewards, penalties)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.1/specs/core/0_beacon-chain.md#rewards-and-penalties
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#rewards-and-penalties
|
||||
func process_rewards_and_penalties(
|
||||
state: var BeaconState, cache: var StateCache) =
|
||||
let
|
||||
|
|
|
@ -42,10 +42,7 @@ func toSlot*(t: BeaconTime): Slot =
|
|||
Slot(uint64(t) div SECONDS_PER_SLOT)
|
||||
|
||||
func toBeaconTime*(c: BeaconClock, t: Time): BeaconTime =
|
||||
doAssert t > c.genesis,
|
||||
"Cannot represent time before genesis, fix BeaconClock"
|
||||
|
||||
BeaconTime(times.seconds(t - c.genesis).uint64)
|
||||
BeaconTime(times.seconds(t - c.genesis).int64)
|
||||
|
||||
func toSlot*(c: BeaconClock, t: Time): Slot =
|
||||
c.toBeaconTime(t).toSlot()
|
||||
|
|
|
@ -80,11 +80,6 @@ proc addBlock*(
|
|||
let proposer_index = get_beacon_proposer_index(state)
|
||||
state.slot -= 1
|
||||
|
||||
# Ferret out remaining GENESIS_EPOCH == 0 assumptions in test code
|
||||
doAssert allIt(
|
||||
body.attestations,
|
||||
it.data.previous_crosslink.epoch >= GENESIS_EPOCH)
|
||||
|
||||
let
|
||||
# Index from the new state, but registry from the old state.. hmm...
|
||||
proposer = state.validator_registry[proposer_index]
|
||||
|
@ -182,7 +177,7 @@ proc makeAttestation*(
|
|||
Attestation(
|
||||
data: data,
|
||||
aggregation_bitfield: aggregation_bitfield,
|
||||
aggregate_signature: sig,
|
||||
signature: sig,
|
||||
custody_bitfield: BitField.init(sac.committee.len)
|
||||
)
|
||||
|
||||
|
|
Loading…
Reference in New Issue