mirror of
https://github.com/status-im/nimbus-eth2.git
synced 2025-02-02 09:46:26 +00:00
restore network sim finalization
* relax attestation validation when attestation is incoming but make it more strict when adding to block * share attestation validation logic between attestation pool and state transition * remove a bunch of redundant logging * fix potential underflow in attestation delay checking * fix committee used for attestation shard selection when attesting * fix attestation data construction
This commit is contained in:
parent
225465578c
commit
78f7a4a505
@ -6,6 +6,9 @@ import
|
||||
beacon_node_types
|
||||
|
||||
proc init*(T: type AttestationPool, blockPool: BlockPool): T =
|
||||
# TODO blockPool is only used when resolving orphaned attestations - it should
|
||||
# probably be removed as a dependency of AttestationPool (or some other
|
||||
# smart refactoring)
|
||||
T(
|
||||
slots: initDeque[SlotData](),
|
||||
blockPool: blockPool,
|
||||
@ -32,81 +35,39 @@ proc combine*(tgt: var Attestation, src: Attestation, flags: UpdateFlags) =
|
||||
debug "Ignoring overlapping attestations"
|
||||
|
||||
proc validate(
|
||||
state: BeaconState, attestation: Attestation, flags: UpdateFlags): bool =
|
||||
# TODO these validations should probably be done elsewhere, and really bad
|
||||
# attestations should probably cause some sort of feedback to the network
|
||||
# layer so they don't spread further.. is there a sliding scale here of
|
||||
# badness?
|
||||
|
||||
# TODO half of this stuff is from beaconstate.validateAttestation - merge?
|
||||
|
||||
let attestationSlot = get_attestation_data_slot(state, attestation.data)
|
||||
|
||||
if attestationSlot <
|
||||
state.finalized_checkpoint.epoch.compute_start_slot_of_epoch():
|
||||
debug "Old attestation",
|
||||
attestationSlot = shortLog(attestationSlot),
|
||||
attestationEpoch = shortLog(attestationSlot.compute_epoch_of_slot),
|
||||
stateSlot = shortLog(state.slot),
|
||||
finalizedEpoch = shortLog(state.finalized_checkpoint.epoch)
|
||||
|
||||
state: BeaconState, attestation: Attestation): bool =
|
||||
# TODO what constitutes a valid attestation when it's about to be added to
|
||||
# the pool? we're interested in attestations that will become viable
|
||||
# for inclusion in blocks in the future and on any fork, so we need to
|
||||
# consider that validations might happen using the state of a different
|
||||
# fork.
|
||||
# Some things are always invalid (like out-of-bounds issues etc), but
|
||||
# others are more subtle - how do we validate the signature for example?
|
||||
# It might be valid on one fork but not another. One thing that helps
|
||||
# is that committees are stable per epoch and that it should be OK to
|
||||
# include an attestation in a block even if the corresponding validator
|
||||
# was slashed in the same epoch - there's no penalty for doing this and
|
||||
# the vote counting logic will take care of any ill effects (TODO verify)
|
||||
let data = attestation.data
|
||||
if not (data.crosslink.shard < SHARD_COUNT):
|
||||
notice "Attestation shard too high",
|
||||
attestation_shard = data.crosslink.shard
|
||||
return
|
||||
|
||||
# TODO what makes sense here? If an attestation is from the future with
|
||||
# regards to the state, something is wrong - it's a bad attestation, we're
|
||||
# desperatly behind or someone is sending bogus attestations...
|
||||
if attestationSlot > state.slot + 64:
|
||||
debug "Future attestation",
|
||||
attestationSlot = shortLog(attestationSlot),
|
||||
attestationEpoch = shortLog(attestationSlot.compute_epoch_of_slot),
|
||||
stateSlot = shortLog(state.slot),
|
||||
finalizedEpoch = shortLog(state.finalized_checkpoint.epoch)
|
||||
# Without this check, we can't get a slot number for the attestation as
|
||||
# certain helpers will assert
|
||||
# TODO this could probably be avoided by being smart about the specific state
|
||||
# used to validate the attestation: most likely if we pick the state of
|
||||
# the beacon block being voted for and a slot in the target epoch
|
||||
# of the attestation, we'll be safe!
|
||||
# TODO the above state selection logic should probably live here in the
|
||||
# attestation pool
|
||||
if not (data.target.epoch == get_previous_epoch(state) or
|
||||
data.target.epoch == get_current_epoch(state)):
|
||||
notice "Target epoch not current or previous epoch"
|
||||
|
||||
return
|
||||
|
||||
if not attestation.custody_bits.BitSeq.isZeros:
|
||||
notice "Invalid custody bitfield for phase 0"
|
||||
return false
|
||||
|
||||
if attestation.aggregation_bits.BitSeq.isZeros:
|
||||
notice "Empty aggregation bitfield"
|
||||
return false
|
||||
|
||||
## the rest; turns into expensive NOP until then.
|
||||
if skipValidation notin flags:
|
||||
let
|
||||
participants = get_attesting_indices_seq(
|
||||
state, attestation.data, attestation.aggregation_bits)
|
||||
|
||||
## TODO when the custody_bits 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
|
||||
|
||||
group_public_key = bls_aggregate_pubkeys(
|
||||
participants.mapIt(state.validators[it].pubkey))
|
||||
|
||||
# Verify that aggregate_signature verifies using the group pubkey.
|
||||
if not bls_verify_multiple(
|
||||
@[
|
||||
bls_aggregate_pubkeys(mapIt(custody_bit_0_participants,
|
||||
state.validators[it].pubkey)),
|
||||
bls_aggregate_pubkeys(mapIt(custody_bit_1_participants,
|
||||
state.validators[it].pubkey)),
|
||||
],
|
||||
@[
|
||||
hash_tree_root(AttestationDataAndCustodyBit(
|
||||
data: attestation.data, custody_bit: false)),
|
||||
hash_tree_root(AttestationDataAndCustodyBit(
|
||||
data: attestation.data, custody_bit: true)),
|
||||
],
|
||||
attestation.signature,
|
||||
get_domain(state, DOMAIN_ATTESTATION,
|
||||
compute_epoch_of_slot(get_attestation_data_slot(state, attestation.data))),
|
||||
):
|
||||
notice "Invalid signature", participants
|
||||
return false
|
||||
|
||||
true
|
||||
|
||||
proc slotIndex(
|
||||
@ -173,14 +134,19 @@ proc updateLatestVotes(
|
||||
|
||||
proc add*(pool: var AttestationPool,
|
||||
state: BeaconState,
|
||||
blck: BlockRef,
|
||||
attestation: Attestation) =
|
||||
# TODO there are constraints on the state and block being passed in here
|
||||
# but what these are is unclear.. needs analyzing from a high-level
|
||||
# perspective / spec intent
|
||||
# TODO should update the state correctly in here instead of forcing the caller
|
||||
# to do it...
|
||||
doAssert blck.root == attestation.data.beacon_block_root
|
||||
var cache = get_empty_per_epoch_cache()
|
||||
|
||||
# TODO should validate against the state of the block being attested to?
|
||||
if not validate(state, attestation, {skipValidation}):
|
||||
debug "attestationPool:add:notValidate",
|
||||
if not validate(state, attestation):
|
||||
notice "Invalid attestation",
|
||||
attestationData = shortLog(attestation.data),
|
||||
validatorIndex = get_attesting_indices(state, attestation.data, attestation.aggregation_bits, cache),
|
||||
current_epoch = get_current_epoch(state),
|
||||
target_epoch = attestation.data.target.epoch,
|
||||
stateSlot = state.slot
|
||||
@ -220,15 +186,16 @@ proc add*(pool: var AttestationPool,
|
||||
if not found:
|
||||
# Attestations in the pool that are a subset of the new attestation
|
||||
# can now be removed per same logic as above
|
||||
|
||||
debug "Removing subset attestations",
|
||||
existingParticipants = a.validations.filterIt(
|
||||
it.aggregation_bits.isSubsetOf(validation.aggregation_bits)
|
||||
).mapIt(get_attesting_indices_seq(
|
||||
state, a.data, it.aggregation_bits)),
|
||||
newParticipants = participants
|
||||
|
||||
a.validations.keepItIf(
|
||||
if it.aggregation_bits.isSubsetOf(validation.aggregation_bits):
|
||||
debug "Removing subset attestation",
|
||||
existingParticipants = get_attesting_indices_seq(
|
||||
state, a.data, it.aggregation_bits),
|
||||
newParticipants = participants
|
||||
false
|
||||
else:
|
||||
true)
|
||||
not it.aggregation_bits.isSubsetOf(validation.aggregation_bits))
|
||||
|
||||
a.validations.add(validation)
|
||||
pool.updateLatestVotes(state, attestationSlot, participants, a.blck)
|
||||
@ -236,7 +203,8 @@ proc add*(pool: var AttestationPool,
|
||||
info "Attestation resolved",
|
||||
attestationData = shortLog(attestation.data),
|
||||
validations = a.validations.len(),
|
||||
validatorIndex = get_attesting_indices(state, attestation.data, attestation.aggregation_bits, cache),
|
||||
validatorIndex = get_attesting_indices(
|
||||
state, attestation.data, attestation.aggregation_bits, cache),
|
||||
current_epoch = get_current_epoch(state),
|
||||
target_epoch = attestation.data.target.epoch,
|
||||
stateSlot = state.slot
|
||||
@ -246,33 +214,32 @@ proc add*(pool: var AttestationPool,
|
||||
break
|
||||
|
||||
if not found:
|
||||
if (let blck = pool.blockPool.getOrResolve(
|
||||
attestation.data.beacon_block_root); blck != nil):
|
||||
slotData.attestations.add(AttestationEntry(
|
||||
data: attestation.data,
|
||||
blck: blck,
|
||||
validations: @[validation]
|
||||
))
|
||||
pool.updateLatestVotes(state, attestationSlot, participants, blck)
|
||||
slotData.attestations.add(AttestationEntry(
|
||||
data: attestation.data,
|
||||
blck: blck,
|
||||
validations: @[validation]
|
||||
))
|
||||
pool.updateLatestVotes(state, attestationSlot, participants, blck)
|
||||
|
||||
info "Attestation resolved",
|
||||
attestationData = shortLog(attestation.data),
|
||||
validatorIndex = get_attesting_indices(state, attestation.data, attestation.aggregation_bits, cache),
|
||||
current_epoch = get_current_epoch(state),
|
||||
target_epoch = attestation.data.target.epoch,
|
||||
stateSlot = state.slot,
|
||||
validations = 1
|
||||
info "Attestation resolved",
|
||||
attestationData = shortLog(attestation.data),
|
||||
validatorIndex = get_attesting_indices(
|
||||
state, attestation.data, attestation.aggregation_bits, cache),
|
||||
current_epoch = get_current_epoch(state),
|
||||
target_epoch = attestation.data.target.epoch,
|
||||
stateSlot = state.slot,
|
||||
validations = 1
|
||||
|
||||
else:
|
||||
pool.unresolved[attestation.data.beacon_block_root] =
|
||||
UnresolvedAttestation(
|
||||
attestation: attestation,
|
||||
)
|
||||
proc addUnresolved*(pool: var AttestationPool, attestation: Attestation) =
|
||||
pool.unresolved[attestation.data.beacon_block_root] =
|
||||
UnresolvedAttestation(
|
||||
attestation: attestation,
|
||||
)
|
||||
|
||||
proc getAttestationsForBlock*(
|
||||
pool: AttestationPool, state: var BeaconState,
|
||||
pool: AttestationPool, state: BeaconState,
|
||||
newBlockSlot: Slot): seq[Attestation] =
|
||||
if newBlockSlot - GENESIS_SLOT < MIN_ATTESTATION_INCLUSION_DELAY:
|
||||
if newBlockSlot < (GENESIS_SLOT + MIN_ATTESTATION_INCLUSION_DELAY):
|
||||
debug "Too early for attestations",
|
||||
newBlockSlot = shortLog(newBlockSlot)
|
||||
return
|
||||
@ -313,14 +280,21 @@ proc getAttestationsForBlock*(
|
||||
signature: a.validations[0].aggregate_signature
|
||||
)
|
||||
|
||||
if not validate(state, attestation):
|
||||
warn "Attestation no longer validates..."
|
||||
continue
|
||||
|
||||
# TODO what's going on here is that when producing a block, we need to
|
||||
# include only such attestations that will not cause block validation
|
||||
# to fail. How this interacts with voting and the acceptance of
|
||||
# attestations into the pool in general is an open question that needs
|
||||
# revisiting - for example, when attestations are added, against which
|
||||
# state should they be validated, if at all?
|
||||
if not process_attestation(
|
||||
state, attestation, {skipValidation, nextSlot}, cache):
|
||||
# TODO we're checking signatures here every time which is very slow - this
|
||||
# is needed because validate does nothing for now and we don't want
|
||||
# to include a broken attestation
|
||||
if not check_attestation(
|
||||
state, attestation, {nextSlot}, cache):
|
||||
continue
|
||||
|
||||
for v in a.validations[1..^1]:
|
||||
@ -350,26 +324,28 @@ proc getAttestationsForTargetEpoch*(
|
||||
for s in begin_slot .. end_slot_minus1:
|
||||
result.add getAttestationsForBlock(pool, state, s.Slot)
|
||||
|
||||
proc resolve*(pool: var AttestationPool, state: BeaconState) =
|
||||
var done: seq[Eth2Digest]
|
||||
var resolved: seq[Attestation]
|
||||
proc resolve*(pool: var AttestationPool, cache: var StateData) =
|
||||
var
|
||||
done: seq[Eth2Digest]
|
||||
resolved: seq[tuple[blck: BlockRef, attestation: Attestation]]
|
||||
|
||||
for k, v in pool.unresolved.mpairs():
|
||||
let attestation_slot = get_attestation_data_slot(state, v.attestation.data)
|
||||
if v.tries > 8 or attestation_slot < pool.startingSlot:
|
||||
if (let blck = pool.blockPool.getRef(k); not blck.isNil()):
|
||||
resolved.add((blck, v.attestation))
|
||||
done.add(k)
|
||||
elif v.tries > 8:
|
||||
done.add(k)
|
||||
else:
|
||||
if pool.blockPool.get(k).isSome():
|
||||
resolved.add(v.attestation)
|
||||
done.add(k)
|
||||
else:
|
||||
inc v.tries
|
||||
inc v.tries
|
||||
|
||||
for k in done:
|
||||
pool.unresolved.del(k)
|
||||
|
||||
for a in resolved:
|
||||
pool.add(state, a)
|
||||
pool.blockPool.updateStateData(
|
||||
cache, BlockSlot(blck: a.blck, slot: a.blck.slot))
|
||||
|
||||
pool.add(cache.data.data, a.blck, a.attestation)
|
||||
|
||||
proc latestAttestation*(
|
||||
pool: AttestationPool, pubKey: ValidatorPubKey): BlockRef =
|
||||
|
@ -271,22 +271,21 @@ proc getAttachedValidator(
|
||||
|
||||
proc updateHead(node: BeaconNode, slot: Slot): BlockRef =
|
||||
# Use head state for attestation resolution below
|
||||
# TODO do we need to resolve attestations using all available head states?
|
||||
node.blockPool.withState(
|
||||
node.stateCache, BlockSlot(blck: node.blockPool.head.blck, slot: slot)):
|
||||
# Check pending attestations - maybe we found some blocks for them
|
||||
node.attestationPool.resolve(state)
|
||||
|
||||
# TODO move all of this logic to BlockPool
|
||||
debug "Preparing for fork choice",
|
||||
stateRoot = shortLog(root),
|
||||
connectedPeers = node.network.peersCount,
|
||||
stateSlot = shortLog(state.slot),
|
||||
stateEpoch = shortLog(state.slot.computeEpochOfSlot)
|
||||
# Check pending attestations - maybe we found some blocks for them
|
||||
node.attestationPool.resolve(node.stateCache)
|
||||
|
||||
# TODO move all of this logic to BlockPool
|
||||
|
||||
let
|
||||
justifiedHead = node.blockPool.latestJustifiedBlock()
|
||||
|
||||
debug "Preparing for fork choice",
|
||||
justifiedHeadRoot = shortLog(justifiedHead.root),
|
||||
justifiedHeadSlot = shortLog(justifiedHead.slot),
|
||||
justifiedHeadEpoch = shortLog(justifiedHead.slot.compute_epoch_of_slot),
|
||||
connectedPeers = node.network.peersCount
|
||||
|
||||
# TODO slot number is wrong here, it should be the start of the epoch that
|
||||
# got finalized:
|
||||
# https://github.com/ethereum/eth2.0-specs/issues/768
|
||||
@ -295,6 +294,7 @@ proc updateHead(node: BeaconNode, slot: Slot): BlockRef =
|
||||
BlockSlot(blck: justifiedHead, slot: justifiedHead.slot)):
|
||||
|
||||
lmdGhost(node.attestationPool, state, justifiedHead)
|
||||
|
||||
info "Fork chosen",
|
||||
newHeadSlot = shortLog(newHead.slot),
|
||||
newHeadEpoch = shortLog(newHead.slot.computeEpochOfSlot),
|
||||
@ -414,37 +414,30 @@ proc onAttestation(node: BeaconNode, attestation: Attestation) =
|
||||
attestationData = shortLog(attestation.data),
|
||||
signature = shortLog(attestation.signature)
|
||||
|
||||
let
|
||||
wallSlot = node.beaconClock.now().toSlot()
|
||||
head = node.blockPool.head
|
||||
if (let attestedBlock = node.blockPool.getOrResolve(
|
||||
attestation.data.beacon_block_root); attestedBlock != nil):
|
||||
let
|
||||
wallSlot = node.beaconClock.now().toSlot()
|
||||
head = node.blockPool.head
|
||||
|
||||
if not wallSlot.afterGenesis or wallSlot.slot < head.blck.slot:
|
||||
warn "Received attestation before genesis or head - clock is wrong?",
|
||||
afterGenesis = wallSlot.afterGenesis,
|
||||
wallSlot = shortLog(wallSlot.slot),
|
||||
headSlot = shortLog(head.blck.slot)
|
||||
return
|
||||
if not wallSlot.afterGenesis or wallSlot.slot < head.blck.slot:
|
||||
warn "Received attestation before genesis or head - clock is wrong?",
|
||||
afterGenesis = wallSlot.afterGenesis,
|
||||
wallSlot = shortLog(wallSlot.slot),
|
||||
headSlot = shortLog(head.blck.slot)
|
||||
return
|
||||
|
||||
# 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
|
||||
# the attestation for some of the check? Consider interop with block
|
||||
# production!
|
||||
node.blockPool.withState(node.stateCache,
|
||||
BlockSlot(blck: head.blck, slot: wallSlot.slot)):
|
||||
var stateCache = get_empty_per_epoch_cache()
|
||||
node.attestationPool.add(state, attestation)
|
||||
# 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
|
||||
# the attestation for some of the check? Consider interop with block
|
||||
# production!
|
||||
let
|
||||
bs = BlockSlot(blck: head.blck, slot: wallSlot.slot)
|
||||
|
||||
debug "Attestation received 2",
|
||||
start_attestation_data_slot =
|
||||
get_attestation_data_slot(state, attestation.data),
|
||||
indexed_attesters =
|
||||
get_indexed_attestation(state, attestation, stateCache),
|
||||
target_epoch =
|
||||
attestation.data.target.epoch,
|
||||
cur_epoch = get_current_epoch(state),
|
||||
cur_slot = state.slot
|
||||
|
||||
#doAssert get_current_epoch(state) < attestation.data.target.epoch or state.slot <= get_attestation_data_slot(state, attestation.data)
|
||||
node.blockPool.withState(node.stateCache, bs):
|
||||
node.attestationPool.add(state, attestedBlock, attestation)
|
||||
else:
|
||||
node.attestationPool.addUnresolved(attestation)
|
||||
|
||||
proc onBeaconBlock(node: BeaconNode, blck: BeaconBlock) =
|
||||
# We received a block but don't know much about it yet - in particular, we
|
||||
@ -494,7 +487,6 @@ proc handleAttestations(node: BeaconNode, head: BlockRef, slot: Slot) =
|
||||
attestationHeadRoot = shortLog(attestationHead.blck.root),
|
||||
attestationSlot = shortLog(slot)
|
||||
|
||||
|
||||
# Collect data to send before node.stateCache grows stale
|
||||
var attestations: seq[tuple[
|
||||
data: AttestationData, committeeLen, indexInCommittee: int,
|
||||
@ -503,30 +495,30 @@ proc handleAttestations(node: BeaconNode, head: BlockRef, slot: Slot) =
|
||||
# We need to run attestations exactly for the slot that we're attesting to.
|
||||
# In case blocks went missing, this means advancing past the latest block
|
||||
# using empty slots as fillers.
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.8.2/specs/validator/0_beacon-chain-validator.md#validator-assignments
|
||||
# TODO we could cache the validator assignment since it's valid for the entire
|
||||
# epoch since it doesn't change, but that has to be weighed against
|
||||
# the complexity of handling forks correctly - instead, we use an adapted
|
||||
# version here that calculates the committee for a single slot only
|
||||
node.blockPool.withState(node.stateCache, attestationHead):
|
||||
var cache = get_empty_per_epoch_cache()
|
||||
let epoch = compute_epoch_of_slot(slot)
|
||||
for committee_index in 0'u64 ..< get_committee_count(state, epoch):
|
||||
## TODO verify that this is the correct mapping; it's consistent with
|
||||
## other code
|
||||
let
|
||||
epoch = compute_epoch_of_slot(slot)
|
||||
committees_per_slot = get_committee_count(state, epoch) div SLOTS_PER_EPOCH
|
||||
start_slot = compute_start_slot_of_epoch(epoch)
|
||||
offset = committees_per_slot * (slot mod SLOTS_PER_EPOCH)
|
||||
slot_start_shard = (get_start_shard(state, epoch) + offset) mod SHARD_COUNT
|
||||
|
||||
for i in 0'u64..<committees_per_slot:
|
||||
let
|
||||
shard = committee_index mod SHARD_COUNT
|
||||
shard2 = (committee_index + get_start_shard(state, epoch)) mod SHARD_COUNT
|
||||
committee = get_crosslink_committee(state, epoch, shard2, cache)
|
||||
shard = Shard((slot_start_shard + i) mod SHARD_COUNT)
|
||||
committee = get_crosslink_committee(state, epoch, shard, cache)
|
||||
|
||||
for i, validatorIdx in committee:
|
||||
let validator = node.getAttachedValidator(state, validatorIdx)
|
||||
if validator != nil:
|
||||
let ad = makeAttestationData(state, shard2, blck.root)
|
||||
attestations.add (
|
||||
ad,
|
||||
committee.len, i, validator)
|
||||
debug "handleAttestations: adding attestation to list for broadcast",
|
||||
data=ad,
|
||||
epoch=epoch,
|
||||
committeeIdx = i,
|
||||
validatorIdx = validatorIdx.int,
|
||||
shard=shard2,
|
||||
committee = committee
|
||||
let ad = makeAttestationData(state, shard, blck.root)
|
||||
attestations.add((ad, committee.len, i, validator))
|
||||
|
||||
for a in attestations:
|
||||
traceAsyncErrors sendAttestation(
|
||||
@ -543,35 +535,10 @@ proc handleProposal(node: BeaconNode, head: BlockRef, slot: Slot):
|
||||
# revisit this - we should be able to advance behind
|
||||
var cache = get_empty_per_epoch_cache()
|
||||
node.blockPool.withState(node.stateCache, BlockSlot(blck: head, slot: slot)):
|
||||
# justification won't happen in odd case anyway
|
||||
let prev_epoch = get_previous_epoch(state)
|
||||
let prev_epoch_attestations = node.attestationPool.getAttestationsForTargetEpoch(state, prev_epoch)
|
||||
|
||||
debug "handleProposal: getAttestationsForTargetEpoch attesting indices for prev_epoch",
|
||||
attesting_indices = mapIt(prev_epoch_attestations, get_attesting_indices(state, it.data, it.aggregation_bits, cache))
|
||||
|
||||
let
|
||||
proposerIdx = get_beacon_proposer_index(state, cache)
|
||||
validator = node.getAttachedValidator(state, proposerIdx)
|
||||
|
||||
# Ugly hack.
|
||||
# TODO handle MAX_ATTESTATIONS & merging pointless dupes for this purpose?
|
||||
blockBody2 = BeaconBlockBody(
|
||||
attestations:
|
||||
#node.attestationPool.getAttestationsForBlock(state, slot - min(MIN_ATTESTATION_INCLUSION_DELAY.uint64, slot.uint64)))
|
||||
prev_epoch_attestations)
|
||||
|
||||
newBlock2 = BeaconBlock(
|
||||
slot: slot,
|
||||
parent_root: head.root,
|
||||
body: blockBody2
|
||||
)
|
||||
|
||||
if newBlock2.slot > 0 and not processAttestations(state, newBlock2, {skipValidation}, cache):
|
||||
debug "when calling processAttestations from handleAttestations, failed"
|
||||
else:
|
||||
debug "when calling processAttestations from handleAttestations, succeeded"
|
||||
|
||||
if validator != nil:
|
||||
return await proposeBlock(node, validator, head, slot)
|
||||
|
||||
|
@ -467,8 +467,9 @@ func get_indexed_attestation*(state: BeaconState, attestation: Attestation,
|
||||
)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.8.1/specs/core/0_beacon-chain.md#attestations
|
||||
proc process_attestation*(
|
||||
state: var BeaconState, attestation: Attestation, flags: UpdateFlags,
|
||||
|
||||
proc check_attestation*(
|
||||
state: BeaconState, attestation: Attestation, flags: UpdateFlags,
|
||||
stateCache: var StateCache): bool =
|
||||
## 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
|
||||
@ -480,7 +481,7 @@ proc process_attestation*(
|
||||
|
||||
let data = attestation.data
|
||||
|
||||
debug "process_attestation: beginning",
|
||||
trace "process_attestation: beginning",
|
||||
attestation=attestation
|
||||
|
||||
if not (data.crosslink.shard < SHARD_COUNT):
|
||||
@ -507,13 +508,6 @@ proc process_attestation*(
|
||||
state_slot = shortLog(stateSlot))
|
||||
return
|
||||
|
||||
let pending_attestation = PendingAttestation(
|
||||
data: data,
|
||||
aggregation_bits: attestation.aggregation_bits,
|
||||
inclusion_delay: state.slot - attestation_slot,
|
||||
proposer_index: get_beacon_proposer_index(state, stateCache),
|
||||
)
|
||||
|
||||
# Check FFG data, crosslink data, and signature
|
||||
let ffg_check_data = (data.source.epoch, data.source.root, data.target.epoch)
|
||||
|
||||
@ -528,11 +522,6 @@ proc process_attestation*(
|
||||
hash_tree_root(state.current_crosslinks[data.crosslink.shard])):
|
||||
warn("Crosslink shard's current crosslinks not matching crosslink parent root")
|
||||
return
|
||||
|
||||
debug "process_attestation: current_epoch_attestations.add",
|
||||
pending_attestation = pending_attestation,
|
||||
validator_index = get_attesting_indices(state, attestation.data, attestation.aggregation_bits, cache)
|
||||
state.current_epoch_attestations.add(pending_attestation)
|
||||
else:
|
||||
if not (ffg_check_data == (state.previous_justified_checkpoint.epoch,
|
||||
state.previous_justified_checkpoint.root, get_previous_epoch(state))):
|
||||
@ -544,11 +533,6 @@ proc process_attestation*(
|
||||
warn("Crosslink shard's previous crosslinks not matching crosslink parent root")
|
||||
return
|
||||
|
||||
debug "process_attestation: previous_epoch_attestations.add",
|
||||
pending_attestation = pending_attestation,
|
||||
validator_index = get_attesting_indices(state, attestation.data, attestation.aggregation_bits, cache)
|
||||
state.previous_epoch_attestations.add(pending_attestation)
|
||||
|
||||
let parent_crosslink = if data.target.epoch == get_current_epoch(state):
|
||||
state.current_crosslinks[data.crosslink.shard]
|
||||
else:
|
||||
@ -583,43 +567,71 @@ proc process_attestation*(
|
||||
|
||||
true
|
||||
|
||||
proc process_attestation*(
|
||||
state: var BeaconState, attestation: Attestation, flags: UpdateFlags,
|
||||
stateCache: var StateCache): bool =
|
||||
# In the spec, attestation validation is mixed with state mutation, so here
|
||||
# we've split it into two functions so that the validation logic can be
|
||||
# reused when looking for suitable blocks to include in attestations.
|
||||
# TODO don't log warnings when looking for attestations (return
|
||||
# Result[void, cstring] instead of logging in check_attestation?)
|
||||
if check_attestation(state, attestation, flags, stateCache):
|
||||
let
|
||||
attestation_slot = get_attestation_data_slot(state, attestation.data)
|
||||
pending_attestation = PendingAttestation(
|
||||
data: attestation.data,
|
||||
aggregation_bits: attestation.aggregation_bits,
|
||||
inclusion_delay: state.slot - attestation_slot,
|
||||
proposer_index: get_beacon_proposer_index(state, stateCache),
|
||||
)
|
||||
|
||||
if attestation.data.target.epoch == get_current_epoch(state):
|
||||
trace "process_attestation: current_epoch_attestations.add",
|
||||
pending_attestation = pending_attestation,
|
||||
indices = get_attesting_indices(
|
||||
state, attestation.data, attestation.aggregation_bits, stateCache).len
|
||||
state.current_epoch_attestations.add(pending_attestation)
|
||||
else:
|
||||
trace "process_attestation: previous_epoch_attestations.add",
|
||||
pending_attestation = pending_attestation,
|
||||
indices = get_attesting_indices(
|
||||
state, attestation.data, attestation.aggregation_bits, stateCache).len
|
||||
state.previous_epoch_attestations.add(pending_attestation)
|
||||
|
||||
true
|
||||
else:
|
||||
false
|
||||
|
||||
proc makeAttestationData*(
|
||||
state: BeaconState, shard_offset: uint64,
|
||||
state: BeaconState, shard: uint64,
|
||||
beacon_block_root: Eth2Digest): AttestationData =
|
||||
## Fine points:
|
||||
## Head must be the head state during the slot that validator is
|
||||
## part of committee - notably, it can't be a newer or older state (!)
|
||||
## Create an attestation / vote for the block `beacon_block_root` using the
|
||||
## data in `state` to fill in the rest of the fields.
|
||||
## `state` is the state corresponding to the `beacon_block_root` advanced to
|
||||
## the slot we're attesting to.
|
||||
|
||||
## https://github.com/ethereum/eth2.0-specs/blob/v0.8.2/specs/validator/0_beacon-chain-validator.md#construct-attestation
|
||||
|
||||
let
|
||||
epoch_start_slot = compute_start_slot_of_epoch(compute_epoch_of_slot(state.slot))
|
||||
#shard = (shard_offset + get_start_shard(state,
|
||||
# compute_epoch_of_slot(state.slot))) mod SHARD_COUNT
|
||||
shard = shard_offset
|
||||
# TODO incorrect epoch for wraparound cases
|
||||
target_epoch = compute_epoch_of_slot(state.slot)
|
||||
# TODO wrong target_root when epoch_start_slot == state.slot
|
||||
target_root =
|
||||
if epoch_start_slot == state.slot: beacon_block_root
|
||||
else: get_block_root_at_slot(state, epoch_start_slot)
|
||||
|
||||
debug "makeAttestationData",
|
||||
target_epoch=target_epoch,
|
||||
target_root=target_root,
|
||||
state_slot = state.slot,
|
||||
epoch_start_slot = epoch_start_slot,
|
||||
beacon_block_root = beacon_block_root
|
||||
current_epoch = get_current_epoch(state)
|
||||
start_slot = compute_start_slot_of_epoch(current_epoch)
|
||||
epoch_boundary_block_root =
|
||||
if start_slot == state.slot: beacon_block_root
|
||||
else: get_block_root_at_slot(state, start_slot)
|
||||
parent_crosslink_end_epoch = state.current_crosslinks[shard].end_epoch
|
||||
|
||||
AttestationData(
|
||||
beacon_block_root: beacon_block_root,
|
||||
source: state.current_justified_checkpoint,
|
||||
target: Checkpoint(
|
||||
root: target_root,
|
||||
epoch: target_epoch
|
||||
epoch: current_epoch,
|
||||
root: epoch_boundary_block_root
|
||||
),
|
||||
crosslink: Crosslink(
|
||||
shard: shard,
|
||||
parent_root: hash_tree_root(state.current_crosslinks[shard]),
|
||||
start_epoch: state.current_crosslinks[shard].end_epoch,
|
||||
end_epoch: target_epoch,
|
||||
start_epoch: parent_crosslink_end_epoch,
|
||||
end_epoch: min(
|
||||
current_epoch, parent_crosslink_end_epoch + MAX_EPOCHS_PER_CROSSLINK),
|
||||
)
|
||||
)
|
||||
|
@ -265,7 +265,7 @@ proc processAttestations*(
|
||||
notice "Attestation: too many!", attestations = blck.body.attestations.len
|
||||
return false
|
||||
|
||||
debug "in processAttestations, not processed attestations",
|
||||
trace "in processAttestations, not processed attestations",
|
||||
attestations_len = blck.body.attestations.len()
|
||||
|
||||
if not blck.body.attestations.allIt(process_attestation(state, it, flags, stateCache)):
|
||||
@ -275,7 +275,7 @@ proc processAttestations*(
|
||||
# Apply the attestations
|
||||
var committee_count_cache = initTable[Epoch, uint64]()
|
||||
|
||||
debug "in processAttestations, has processed attestations",
|
||||
trace "in processAttestations, has processed attestations",
|
||||
attestations_len = blck.body.attestations.len()
|
||||
|
||||
var cache = get_empty_per_epoch_cache()
|
||||
@ -305,7 +305,7 @@ proc processAttestations*(
|
||||
else:
|
||||
state.previous_epoch_attestations.add(pending_attestation)
|
||||
|
||||
debug "processAttestations",
|
||||
trace "processAttestations",
|
||||
target_epoch=attestation.data.target.epoch,
|
||||
current_epoch= get_current_epoch(state),
|
||||
current_epoch_attestations_len=len(get_attesting_indices(state, state.current_epoch_attestations, cache)),
|
||||
|
@ -517,20 +517,20 @@ proc process_final_updates(state: var BeaconState) =
|
||||
SHARD_COUNT
|
||||
|
||||
# Rotate current/previous epoch attestations
|
||||
debug "Rotating epoch attestations",
|
||||
trace "Rotating epoch attestations",
|
||||
current_epoch = get_current_epoch(state)
|
||||
|
||||
|
||||
state.previous_epoch_attestations = state.current_epoch_attestations
|
||||
state.current_epoch_attestations = @[]
|
||||
|
||||
debug "Rotated epoch attestations",
|
||||
trace "Rotated epoch attestations",
|
||||
current_epoch = get_current_epoch(state)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.8.2/specs/core/0_beacon-chain.md#per-epoch-processing
|
||||
proc process_epoch*(state: var BeaconState) =
|
||||
# @proc are placeholders
|
||||
|
||||
debug "process_epoch",
|
||||
trace "process_epoch",
|
||||
current_epoch = get_current_epoch(state)
|
||||
|
||||
var per_epoch_cache = get_empty_per_epoch_cache()
|
||||
@ -538,7 +538,7 @@ proc process_epoch*(state: var BeaconState) =
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.8.2/specs/core/0_beacon-chain.md#justification-and-finalization
|
||||
process_justification_and_finalization(state, per_epoch_cache)
|
||||
|
||||
debug "ran process_justification_and_finalization",
|
||||
trace "ran process_justification_and_finalization",
|
||||
current_epoch = get_current_epoch(state)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.8.2/specs/core/0_beacon-chain.md#crosslinks
|
||||
|
@ -64,12 +64,12 @@ proc process_slots*(state: var BeaconState, slot: Slot) =
|
||||
if (state.slot + 1) mod SLOTS_PER_EPOCH == 0:
|
||||
# Note: Genesis epoch = 0, no need to test if before Genesis
|
||||
process_epoch(state)
|
||||
debug "process_slots: Incrementing slot",
|
||||
trace "process_slots: Incrementing slot",
|
||||
state_slot_now = state.slot,
|
||||
state_slot_next = state.slot + 1,
|
||||
cur_epoch = get_current_epoch(state)
|
||||
state.slot += 1
|
||||
debug "process_slots: Incremented slot",
|
||||
trace "process_slots: Incremented slot",
|
||||
cur_epoch = get_current_epoch(state)
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/specs/core/0_beacon-chain.md#state-root-verification
|
||||
@ -178,7 +178,7 @@ proc process_slots*(state: var HashedBeaconState, slot: Slot) =
|
||||
if (state.data.slot + 1) mod SLOTS_PER_EPOCH == 0:
|
||||
# Note: Genesis epoch = 0, no need to test if before Genesis
|
||||
process_epoch(state.data)
|
||||
debug "process_slots_HashedBeaconState_: Incrementing slot",
|
||||
trace "process_slots_HashedBeaconState_: Incrementing slot",
|
||||
state_slot_now = state.data.slot,
|
||||
state_slot_next = state.data.slot + 1,
|
||||
cur_epoch = get_current_epoch(state.data)
|
||||
|
@ -21,7 +21,7 @@ mkdir -p "${BUILD_OUTPUTS_DIR}"
|
||||
DEFS="-d:chronicles_log_level=DEBUG "
|
||||
DEFS+="-d:SHARD_COUNT=${SHARD_COUNT:-16} " # Spec default: 1024
|
||||
DEFS+="-d:SLOTS_PER_EPOCH=${SLOTS_PER_EPOCH:-16} " # Spec default: 64
|
||||
DEFS+="-d:SECONDS_PER_SLOT=${SECONDS_PER_SLOT:-18} " # Spec default: 6
|
||||
DEFS+="-d:SECONDS_PER_SLOT=${SECONDS_PER_SLOT:-6} " # Spec default: 6
|
||||
|
||||
LAST_VALIDATOR_NUM=$(( NUM_VALIDATORS - 1 ))
|
||||
LAST_VALIDATOR="$VALIDATORS_DIR/v$(printf '%07d' $LAST_VALIDATOR_NUM).deposit.json"
|
||||
|
@ -15,7 +15,7 @@ cd - &>/dev/null
|
||||
: ${BUILD_OUTPUTS_DIR:="$GIT_ROOT/build"}
|
||||
|
||||
NUM_VALIDATORS=${VALIDATORS:-1000}
|
||||
NUM_NODES=${NODES:-2}
|
||||
NUM_NODES=${NODES:-4}
|
||||
NUM_MISSING_NODES=${MISSING_NODES:-0}
|
||||
|
||||
SIMULATION_DIR="${SIM_ROOT}/data"
|
||||
|
@ -44,7 +44,7 @@ suite "Attestation pool processing" & preset():
|
||||
attestation = makeAttestation(
|
||||
state.data.data, state.blck.root, crosslink_committee[0])
|
||||
|
||||
pool.add(state.data.data, attestation)
|
||||
pool.add(state.data.data, state.blck, attestation)
|
||||
|
||||
process_slots(state.data, MIN_ATTESTATION_INCLUSION_DELAY.Slot) # minus 1?
|
||||
|
||||
@ -73,8 +73,8 @@ suite "Attestation pool processing" & preset():
|
||||
state.data.data, state.blck.root, cc1[0])
|
||||
|
||||
# test reverse order
|
||||
pool.add(state.data.data, attestation1)
|
||||
pool.add(state.data.data, attestation0)
|
||||
pool.add(state.data.data, state.blck, attestation1)
|
||||
pool.add(state.data.data, state.blck, attestation0)
|
||||
|
||||
process_slots(state.data, MIN_ATTESTATION_INCLUSION_DELAY.Slot) # minus 1?
|
||||
|
||||
@ -96,8 +96,8 @@ suite "Attestation pool processing" & preset():
|
||||
attestation1 = makeAttestation(
|
||||
state.data.data, state.blck.root, cc0[1])
|
||||
|
||||
pool.add(state.data.data, attestation0)
|
||||
pool.add(state.data.data, attestation1)
|
||||
pool.add(state.data.data, state.blck, attestation0)
|
||||
pool.add(state.data.data, state.blck, attestation1)
|
||||
|
||||
process_slots(state.data, MIN_ATTESTATION_INCLUSION_DELAY.Slot) # minus 1?
|
||||
|
||||
@ -122,8 +122,8 @@ suite "Attestation pool processing" & preset():
|
||||
|
||||
attestation0.combine(attestation1, {skipValidation})
|
||||
|
||||
pool.add(state.data.data, attestation0)
|
||||
pool.add(state.data.data, attestation1)
|
||||
pool.add(state.data.data, state.blck, attestation0)
|
||||
pool.add(state.data.data, state.blck, attestation1)
|
||||
|
||||
process_slots(state.data, MIN_ATTESTATION_INCLUSION_DELAY.Slot) # minus 1?
|
||||
|
||||
@ -147,8 +147,8 @@ suite "Attestation pool processing" & preset():
|
||||
|
||||
attestation0.combine(attestation1, {skipValidation})
|
||||
|
||||
pool.add(state.data.data, attestation1)
|
||||
pool.add(state.data.data, attestation0)
|
||||
pool.add(state.data.data, state.blck, attestation1)
|
||||
pool.add(state.data.data, state.blck, attestation0)
|
||||
|
||||
process_slots(state.data, MIN_ATTESTATION_INCLUSION_DELAY.Slot) # minus 1?
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user