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:
Dustin Brody 2019-06-12 07:48:49 +00:00 committed by GitHub
parent 10c7920b27
commit d400650eeb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 361 additions and 489 deletions

View File

@ -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():

View File

@ -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

View File

@ -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)
)

View File

@ -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)
)

View File

@ -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)

View File

@ -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?

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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)
)