Skip to content

validator: test the client with a large number of validator keys #797

Open

Description

@MegaRedHand

The validator client has only been run with a handful of keys. Nothing measures what it costs per slot, per epoch or at startup with thousands of them, which is where several of its designs would show it first.

This came up in the review of #707: PreferencesService takes its sent lock once per proposer duty instead of once per poll (crates/validator/src/preferences.rs). That is harmless at the few proposals an epoch holds, but it is one example of a per-key cost nobody has measured.

What grows with the number of keys

These are read from the code, not measured:

  • Startup. ValidatorStore::load decrypts the keystores one after another (keys/store.rs). With EIP-2335's default scrypt parameters each one takes a noticeable fraction of a second, so 10,000 keys could take a long time before the first duty.
  • Slashing protection. Each commit re-renders the whole interchange file with every key's record, then writes it durably: a temporary file, an fsync, a rename and a directory fsync (slashing_protection/file.rs write). A slot's attestations are batched into one commit, so the commit itself is one per slot rather than one per key. Its size still grows with the number of keys, and it sits in front of every attestation signature. time_slashing_protection_write already records how long it takes.
  • Builder registrations (fulu and earlier, with --builder-proposals).
    • The first epoch signs one BLS signature per key on the blocking pool, 64 per chunk.
    • Every epoch then sends every registration in batches of 500.
    • A per-slot keymanager change waits behind that send. Against a slow node, that is at most 12 s per 500 validators (registration.rs module doc).
  • Epoch refresh. validator_indices POSTs every loaded pubkey every epoch, and prepare_beacon_proposer sends one entry per resolved key.
  • Per-slot duties. Attestations, aggregation selection and sync committee messages are signed for every key with a duty in the slot.

Proposal

  • A scale test or benchmark. It would drive the services against MockBeaconNode with, for example, 1,000 and 10,000 keys over a few epochs.
    • The keys would use the small-integer secrets from test_state::secret_key_for, so no keystore has to be decrypted.
    • It would measure the duty path's time per slot, the slashing-protection write time, the registration send's length and memory.
  • A separate startup measurement with real scrypt keystores.
  • A devnet run with a large key set behind one validator client, to see the same costs against a real beacon node.
  • Decide from the numbers whether anything needs changing first: parallel keystore decryption, or an incremental slashing-protection store in place of rewriting the whole file every commit.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions