-
Notifications
You must be signed in to change notification settings - Fork 1k
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
WIP: Add parent_slot to block, attestation, elsewhere #3249
base: dev
Are you sure you want to change the base?
Conversation
A recurring problem of validating blocks and attestations is that the slot of a given block root is unknown until the block itself has been downloaded. This is problematic because clients, when seeing an unknown block root, must start looking for it with neighbours not knowing that the block is already obsolete. For example, when receiving a block or attestation with a *recent* slot but unknown parent/block, clients must attempt to reconstruct the given history - if the history has been orphaned (due to finalization), this is not possible to do, but clients still waste resources and security margin on the attempt, including quarantine spots (where blocks are held while the parent is being resolved) and bandwidth. An important note is that such histories are valid on the network in general: for example, a client that is syncing may not yet have seen the history that finalizes a block and may therefore choose to build on it, thinking that it's the most recent head, even though the network globally has progressed and orphaned it - such a history cannot be discarded trivially. Adding slot information resolves this problem - this PR shows conceptually what the changes could look like. `Attestation` in particular is currently redundantly encoded - because attestations take up significant bandwidth on the network, the PR takes care not to increase the size of the object.
I am uncertain of the justification for this change, not against it, but just fail to really see the use
What does this mean? a client that is syncing cannot build on anything, if it's syncing it can't build. Even if the client believes it's no longer syncing, it can't think it's its most recent head since it can't even validate that block and will have to look for its parent. While I do believe there's a use in case a client receives a very old block that has been orphaned already by a finalized chain, this should be a rather exceptional case of peers sending these very late blocks, there shouldn't be any late blocks at all being gossiped by honest validators. If it is some sort of malicious DoS attack, then validators already can do this providing blocks without any known parent root. and they will be able to also lie on the slot as well: you can't really know for certain that there isn't a fork with a different shuffling |
Imagine the following situation:
Notably, in the above situation, you're acting according to the honest validator spec: the best you can do is build on B (blocks or attestations, doesn't matter). This establishes that it's possible for this to happen just by following the honest validator spec. Now, consider the receiving client that knows about A': they receive a block or attestation with an unknown "parent" B - they have already discarded the B history (or never observed it) because they've seen A' that is final and newer: in order to validate B' (which is from slot T, the current wall time and therefore not discardable by current gossip rules), they have to download B - in the current specs, we can't assume the slot of B from the block root alone - except, this is hard. If only we knew the slot of B, we could discard it out of hand: we already know that A', a block with a higher slot, has finalized, so B is automatically discardable in gossip. This is the basic gist of things: in many places in the current spec, we get an anonymous root without knowing its slot - this can be used to waste client resources and is an unnecessary gap - it doesn't happen in the execution chain (because block numbers are consecutive) - this is merely a way to compensate for the fact that lots of empty slots is a possible, albeit unusual, scenario. |
FWIW I believe the feature you want, at least in the case of blocks, can be computed/estimated from the blocknumber in the payload. |
In the above scenario, we have a gap of 64 empty slots on the consensus side, but blocknumber moved only by 1 - the two are not related that way. Remember: we've only seen a root, we cannot make any assumptions about the slot of that root. |
That's precisely what let's you estimate the parent slot, the fact that you're seeing a block for a slot 64 blocks later and it's block number is not compatible with a current block |
So, consider:
Both A' and B have the same block number - now you receive B' which has the block number of B+1 - so does the unfinalized block based on A' (A''). In this case, you cannot unambiguously discard the block based on block number. Also, attestations do not have a block header available and face a similar problem: we know the slot of the attestation, but not that of the block (which could be finalized already). |
This is getting quite involved 🤣, I don't think that situation can actually happen. So for A' to be finalized, a chain with high participation has to have been built on top of it. If you are in the receiving end and you have seen the finalization of A', then you did get a bunch of blocks that included the attestations enough to finalize A' and are at least 64 slots after A', with many of those slots not skipped. When you receive B' with block number B+1 you can be sure that it can't be based on (A')'s chain since this one has continued for a while. But I think I hijacked your PR without meaning to digress, I'm just not certain if there's much to gain with these changes, that will require a hardfork and different block/attestation structures. I'm trying to balance the changes it will require in our code (which in case of prysm without good generics in Go are a little bit of a pain to add new versions of consensus containers) vs the performance gain we would get from this. |
If I am not mistaken peers to which an Assuming we have minority validator nodes yet syncing and producing attestations at a moment in time, and taking a fact that this feature doesn't protect us from malicious behaviour (pointed out by @potuz) I doubt about bandwidth and CPU gain that this improvement can provide. Maybe you have numbers on how many attestations to outdated histories are hitting your nodes in the network? |
A recurring problem of validating blocks and attestations is that the
slot of a given block root is unknown until the block itself has been
downloaded.
This is problematic because clients, when seeing an unknown block root,
must start looking for it with neighbours not knowing that the block is
already obsolete.
For example, when receiving a block or attestation with a recent slot
but unknown parent/block, clients must attempt to reconstruct the given
history - if the history has been orphaned (due to finalization), this
is not possible to do, but clients still waste resources and security
margin on the attempt, including quarantine spots (where blocks are held
while the parent is being resolved) and bandwidth.
An important note is that such histories are valid on the network in
general: for example, a client that is syncing may not yet have seen the
history that finalizes a block and may therefore choose to build on it,
thinking that it's the most recent head, even though the network
globally has progressed and orphaned it - such a history cannot be
discarded trivially.
Adding slot information resolves this problem - this PR shows
conceptually what the changes could look like.
Attestation
in particular is currently redundantly encoded - becauseattestations take up significant bandwidth on the network, the PR takes
care not to increase the size of the object.
This is a WIP PR to show conceptually what the change could look like - it currently builds on top of #3244, the interesting commit being e838be5