Pending aggregates: When multiple aggregated attestations only differing by the aggregator index are in the pending queue, only process one of them. - #16153
Conversation
…ing by the aggregator index are in the pending queue, only process one of them.
37ecd5a to
e94e7c1
Compare
potuz
left a comment
There was a problem hiding this comment.
LGTM, would feel better with an extra validation of my comment
| } | ||
| } | ||
|
|
||
| initialMissingRootCount := len(missingIndicesByRoot) |
There was a problem hiding this comment.
this is unrelated with the PR
There was a problem hiding this comment.
That's right. That's the reason why it's modified in a totally separated commit.
There was a problem hiding this comment.
should we take it out of the PR?
id at least mention it in the github description, as reviewer, it was confusing to read at first
| if a.AggregateAttestationAndProof().GetAggregatorIndex() != b.AggregateAttestationAndProof().GetAggregatorIndex() { | ||
| return false | ||
| } |
There was a problem hiding this comment.
We need to make sure that one of the aggregates is valid if and only iff all other aggregates are valid
There was a problem hiding this comment.
Moving to draft until your comment is solved.
There was a problem hiding this comment.
With this PR, there is a flaw:
If the first aggregated attestation (for a given slot, committee index and aggregation bits) is received, but with an aggregator index which is not supposed to aggregate for this slot, then this aggregated attestation is inserted into the queue, and all other equivalent aggregated attestations are discarded.
Later, when processing the queue, we finally reject this aggregated attestation because it is not valid.
As a consequence, 0 aggregated attestation of this kind is neither accepted nor broadcasted.
==> This is a bug.
9969761 fixes is issue, by moving all verifications not needing the block (including the aggregator index) before inserting the aggregated attestation into the queue.
Only the verifications needing the block are done when pulling aggregated attestations from the queue.
As a bonus, it considerably reduces the process time when pulling aggregated attestations from the queue.
ac485e3 to
9969761
Compare
e06a6c1 to
ed9dd00
Compare
…ering by aggregator index.
697a234 to
0ce9c60
Compare
| // Verify attestation target root is consistent with the head root. | ||
| // This verification is not in the spec, however we guard against it as it opens us up | ||
| // to weird edge cases during verification. The attestation technically could be used to add value to a block, | ||
| // but it's invalid in the spirit of the protocol. Here we choose safety over profit. |
There was a problem hiding this comment.
I don't understand this good. Moving this offline
| func pendingAggregatesAreEqual(a, b ethpb.SignedAggregateAttAndProof) bool { | ||
| // pendingAggregatesAreEqual checks if two pending aggregate attestations are equal. | ||
| // If filterOnAggregatorIndex is false, two aggregates only differ by their aggregator index will be considered equal. | ||
| func pendingAggregatesAreEqual(a, b ethpb.SignedAggregateAttAndProof, filterOnAggregatorIndex bool) bool { |
There was a problem hiding this comment.
for the record I don't mind boolean controllers, but even some linters would recommend here a named wrapper that has the extra validation. I think it's fine as is.
|
|
||
| locker.Lock() | ||
| defer locker.Unlock() | ||
|
|
There was a problem hiding this comment.
I wonder if this deadlock ever happened in the wild
| } | ||
| } | ||
|
|
||
| initialMissingRootCount := len(missingIndicesByRoot) |
There was a problem hiding this comment.
should we take it out of the PR?
id at least mention it in the github description, as reviewer, it was confusing to read at first
| durationAtts := time.Since(startAtts) | ||
|
|
||
| log.WithFields(logrus.Fields{ | ||
| "blockRoot": fmt.Sprintf("%#x", blockRoot), |
There was a problem hiding this comment.
it looks to me we are just passing blockRoot as an argument just for logging purpose, but you dont really need to pass it, we could get the blockRoot from the attestation it self, it's just attestation.data.block_root
| if filterOnAggregatorIndex { | ||
| if a.AggregateAttestationAndProof().GetAggregatorIndex() != b.AggregateAttestationAndProof().GetAggregatorIndex() { | ||
| return false | ||
| } |
There was a problem hiding this comment.
you may want to add a unit test just for this case as i haven't really see them yet
|
Yeah actually it is in a totally separate commit (that's why I asked to read commit by commit). |
What type of PR is this?
Other
What does this PR do? Why is it needed?
When an (potentially aggregated) attestation is received before the block being voted for, Prysm queues this attestation, then processes the queue when the block has been received.
This behavior is consistent with the Phase0 specification .
Once the block being voted for is processed, previously queued (potentially aggregated) attestations are then processed, and broadcasted.
Processing (potentially aggregated) attestations takes some non negligible time. For this reason, (potentially aggregated) attestations are deduplicated before being introduced into the pending queue, to avoid eventually processing duplicates.
Before this PR, two aggregated attestations were considered duplicated if all of the following conditions were gathered:
Aggregated attestations are then broadcasted.
The final purpose of aggregated attestations is to be packed into the next block by the next proposer.
When packing attestations, the aggregator index is not used any more.
This pull request modifies the deduplication function used in the pending aggregated attestations queue by considering that multiple aggregated attestations only differing by the aggregator index are equivalent (removing
2.of the previous list.)As a consequence, the count of aggregated attestations to be introduced in the pending queue is reduced from 1 aggregated attestation by aggregator to, in the best case, MAX_COMMITTEE_PER_SLOT=64.
Also, only a single aggregated attestation for a given version, slot, committee index and aggregation bits will be re-broadcasted. This is a correct behavior, since no data to be included in a block will be lost. (We can even say that this will reduce by a bit the total networking volume.)
How to test:
Synced new blockandVerified and saved pending attestations to pool. (You can pipe logs intogrep -E "Synced new block|Verified and saved pending attestations to pool".Synced new blocklogs, monitor thesinceSlotStartTimevalue. This should monotonically decrease.Verified and saved pending attestations to pool, monitor thependingAggregateAttAndProofCountvalue. It should be a "honest" value. "honest" is not really quantifiable here, since it depends on the aggregators. But it's likely to be less than5*MAX_COMMITTEE_PER_SLOT=320.Which issues(s) does this PR fix?
Partially fixes:
Other notes for review
Please read commit by commit, with commit messages.
The important commit is b748c04.
Acknowledgements