Skip to content
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] feat: Refactor pubkeyCache into finalized and unfinalized cache #5937

Closed
wants to merge 18 commits into from

Conversation

ensi321
Copy link
Contributor

@ensi321 ensi321 commented Sep 5, 2023

Motivation

To refactor the current pubkeyCache by:

  • Introduce unfinalized pubkey2Index in EpochCache that is fork specific and will be cloned on state.clone()
  • To modify existed pubkey2Index and index2Pubkey in EpochCache such that they only contain validators that are confirmed active or in the activation queue.

This is a prerequisite to fixing #5366 .

Description

  • Introduce class UnfinalizedPubkeyIndexMap which is an alias of immutable.Map
  • Existed pubkey2Index and index2Pubkey are renamed to globalPubkey2index and globalIndex2pubkey.
  • Add new field unfinalizedPubkey2Index: UnfinalizedPubkeyIndexMap in the EpochCache and will be included during state cloning
  • Populating globalPubkey2index and globalIndex2pubkey happens during afterProcessEpoch(). At the same time, UnfinalizedPubkeyIndexMap is pruned for every pubkeys that are inserted into global caches.
  • Introduce interfaces to hide finalized and unfinalized caches from callers. Callers to the cache should not interact with finalized and unfinalized cache directly
  • Introduce CarryoverData to specifically allow caller to pass unfinalized cache into EpochCache.createFromState() if desired

@CLAassistant
Copy link

CLAassistant commented Sep 5, 2023

CLA assistant check
All committers have signed the CLA.

addPubkey(index: ValidatorIndex, pubkey: Uint8Array): void {
this.pubkey2index.set(pubkey, index);
this.index2pubkey[index] = bls.PublicKey.fromBytes(pubkey, CoordType.jacobian); // Optimize for aggregation
this.unfinalizedPubkey2index.set(toMemoryEfficientHexStr(pubkey), index);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This entire feature should be fork gated. If this feature in merged to unstable (best option) it must be developed in a way that before eip6110 activation there is 0 performance penalty both in compute and memory footprint. The simplest path would be to assume that before eip6110 activation all pubkeys being added to this function are finalized

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes perfect sense! Will address it

}

getValidatorIndex(pubkey: Uint8Array | PubkeyHex): ValidatorIndex | undefined {
return this.globalPubkey2index.get(pubkey) || this.unfinalizedPubkey2index.get(toMemoryEfficientHexStr(pubkey));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment below about feature gate-ing at zero cost

@@ -838,3 +901,9 @@ export function createEmptyEpochCacheImmutableData(
index2pubkey: [],
};
}

export function createEmptyCarryoverData(): CarryoverData {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you explain in detail the rationale of this new CarryoverData struct?

  • How does it differentatiate from the rest of caches and sidecars?
  • Why must this be a separate object to existing ones?
    In case of doubt it's best to not change APIs to minimize the diff and cognitive load to mantainers

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you explain in detail the rationale of this new CarryoverData struct?

  • How does it differentatiate from the rest of caches and sidecars?
  • Why must this be a separate object to existing ones?
    In case of doubt it's best to not change APIs to minimize the diff and cognitive load to mantainers

With the new unfinalized cache introduced in EpochCache, I was thinking if 1) EpochCache.createFromState() should allow callers passing in an existed unfinalized cache from other state or should 2) createFromState() create an empty unfinalized cache every time.

I checked every caller of createFromState(), and it seems like we are only creating EpochCache from a “fresh” state ie. no unfinalized cache. However we cannot guarantee if future development will demand creating EpochCache from a CachedBeaconState. It wouldn’t make sense to have a new instance of EpochCache with empty unfinalized cache in this case.

I leaned towards the second option. Instead of having the populating logic specifically for unfinalized cache, I think it makes more sense to have a generic container CarryoverData that contains any data including unfinalizedPubkey2index that need to be populated during createFromState. It is almost identical to EpochCacheImmutableData, in fact it would be perfectly fine if we put unfinalizedPubkey2index in EpochCacheImmutableData but this would defeat the purpose as it is unfinalized and mutable hence a separate container.

I am actually open for 1) because we can always make the additional change later on when we encounter the need for it.

Copy link
Contributor

@dapplion dapplion Sep 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. You should mantain the same assumptions of the EpochCache design, otherwise it feels like scope creep. I would prefer to discuss changes on the EpochCache usage assumptions in another thread (issue).

@dapplion
Copy link
Contributor

dapplion commented Sep 5, 2023

Glad to see this feature moving! Since there won't be spec tests for this sorts of caches soon it would be very valuable to introduce tests to assert the invariants of the cache. Getting it wrong would cause consensus bugs, so best to start investing in testing infra early

@ensi321 ensi321 changed the title Refactor pubkeyCache into finalized and unfinalized cache [WIP] feat: Refactor pubkeyCache into finalized and unfinalized cache Sep 6, 2023
@ensi321
Copy link
Contributor Author

ensi321 commented Sep 13, 2023

Closing the PR as this feature will be developed in a dedicated 6110 branch

@ensi321 ensi321 closed this Sep 13, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants