-
Notifications
You must be signed in to change notification settings - Fork 997
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add previous and current crosslinks #874
Conversation
cc: @mkalinin |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great to me 👍
Works even better. Implemented and tested it in our simulator.
There are a few tiny things more that could be dropped out. Also, I think this PR should update other parts of the spec that rely on latest_crosslinks
.
There is one edge case that worries me. What if, during epoch N+1, a crosslink created from attestations during epoch N is confirmed (during epoch N itself, not enough of the attestations were included, but during epoch N+1 some extra attestations get included bringing it above 2/3) and a crosslink created from attestations during epoch N+1 is confirmed? It seems like both would trigger and so the latest crosslink record would be overwritten twice? Are we sure there aren't weird edge cases here where some part of a shard chain somehow slips through without being part of a crosslink? Also, do we have a hard invariant that for a crosslink to be overwritten, the latest crosslink record of the attestations making the new crosslink need to have the existing crosslink as the previous crosslink specified in the attestation? |
There is an invariant in a block processing part. Attestations created in previous epoch must have a crosslink pointing to
This PR removes previous crosslink verification from epoch transition. This verification happens only when attestation is being added to the state (during block processing). |
Yes, this can happen. It seems that this being able to happen is fundamental if we let both the prev and current epoch get crosslinked rather only attempting to crosslink from the previous epoch. If this does happen, then it would be the case (before the epoch transition) that If it were the case that the crosslinked data from the previous attestations is different than the crosslinked data from the current attestations, then the data vote from "current epoch attestations" would win within the context of the single state transition and externally nothing would be conflicting. |
OK, I guess I'm ok with double-overwriting... But the other weird edge case is that in epoch N, a crosslink A -> B is included, then in epoch N+1 the previous latest crosslink is still A, so A -> C could get included. This would require 1/3 slashing within the committee, but it potentially could happen. So then the invariant that the latest crosslink never gets "reverted", only "updated", gets violated. I would recommend we consider just adding a "latest crosslink hash" as part of the crosslink object, and add a check that the crosslink is updated only if its latest crosslink hash matches. It would make analysis cleaner and resolve all of these weird edge cases. |
Added the enforcement and prevents the corner case you described. In the case that A->C attestation exists, I am still giving crosslink rewards atm. Most likely in this scenario, the A->C attestation was not malicious but just got messed up due to latency in processing. A bit torn on whether "best root" should be rewarded in this case. I think so. This also does remove some optionality. In the case that A->B and A->C both come in during the same epoch, only A->B would be updated and A->C would be dropped. If we process the slots in reverse order A->C would be updated and A->B would be dropped. This is a bit indirect and strange so didn't add it in without first discussing TODO: update validator guide once we are happy with this functionality. |
This is ready for review |
* Remove unnecessary per-field comments, focus on field grouping * Group `Validator` fields relevant to light-clients together (small optimisation for light clients) * Rework grouping of `BeaconState` fields * `deposit_index` => `eth1_deposit_index` Do not merge before: * #695 which specifies custom types for individual container fields, offsetting the removal of comments in some instances * #896 and #843 so that we don't have to continuously maintain the genesis `BeaconState` * #874 which introduces `current_crosslinks` and `previous_crosslinks`
A couple questions:
|
|
A follow up, I think it is cleaner to keep the rewards section separate within epoch processing. |
I'd like to get this merged asap. |
specs/core/0_beacon-chain.md
Outdated
a for a in all_attestations if a.data.previous_crosslink == state.latest_crosslinks[shard] | ||
] | ||
all_roots = [a.data.crosslink_data_root for a in valid_attestations] | ||
def get_winning_root_and_participants(state: BeaconState, slot: Slot, shard: Shard) -> Tuple[Bytes32, Bytes32, List[ValidatorIndex]]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It now returns winning_root, previous_crosslink_root, participants
. Alternatively, we can reuse Crosslink
by creating Crosslink(epoch=FAR_FUTURE_EPOCH, crosslink_data_root=winning_root, previous_crosslink_root=previous_crosslink_root)
. So we can change it function to:
def get_crosslink_candidate_and_participants(state: BeaconState, slot: Slot, shard: Shard) -> Tuple[Crosslink, List[ValidatorIndex]]:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's what I did 👍
specs/core/0_beacon-chain.md
Outdated
attestations = state.current_epoch_attestations if slot_to_epoch(slot) == get_current_epoch(state) else state.previous_epoch_attestations | ||
|
||
valid_attestations = [a for a in attestations if a.data.shard == shard] | ||
all_roots = [(a.data.crosslink_data_root, a.data.previous_crosslink_root) for a in valid_attestations] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto, alternatively:
crosslink_candidates = [Crosslink(epoch=FAR_FUTURE_EPOCH, crosslink_data_root=a.data.crosslink_data_root, previous_crosslink_root=a.data.previous_crosslink_root) for a in valid_attestations]
Refactor idea:
Possibly for another PR once this gets merged :) |
@JustinDrake I added |
Somewhat deep change to address some fundamental bugs in how we handle crosslinks in state. Addresses the issues raised in #855 but through a simplified mechanism. Previous and current crosslinks are kept in state to ensure can always check
attestation.previous_crosslink
validity for attestations from previous and current epoch.Changes:
latest_crosslinks
current_crosslinks
andprevious_crosslinks
-- this ensures that if an attestation is from previous or current epoch, the requisiteprevious_crosslink
data is available in state to validateprevious_crosslink_root
toCrosslink
and strictly enforce that crosslinks must form a chain to prevent cases in which data is crosslinked and then overwritten.process_crosslinks
that checks for validity ofprevious_crosslink
. This is unnecessary due to it already being checked inprocess_crosslink
during block processing.process_crosslinks