Skip to content

Conversation

@shane-moore
Copy link

@shane-moore shane-moore commented Oct 30, 2025

Changes

  • added polling of ptc duties to the VC per spec

How This Works

  • The VC is currently checking whether one of its registered validators are in a voting committee, such as sync or attestation, or if they are the proposer for a slot. With epbs, it's possible for a validator to also be in a ptc committee for at most one slot of the epoch. More context on how validators are selected to be in a ptc was discussed on discord.

Implemented in the PR

duties_service will now spawn a background polling task for the current and next epoch to check whether any of the registered validators are in a ptc committee. Since re-org is possible, we also poll every slot. The beacon node querying is performed by new request endpoint POST validator/duties/ptc/{epoch} per beacon api spec.

To save on bandwidth similar to attestation committee polling, the ptc polling will make a tiny initial requests to learn the dependent_root, compare with the cached dependent_root, and only fetch full duties for the epoch if the root has changed (or if the root is unknown like for a new epoch). Old duties are pruned based on HISTORICAL_DUTIES_EPOCHS.

Todo

  • add tests noted in the diffs
  • create beacon node (server side) response that will send the ptc duties to the VC. Some of this work has already been done in other PR's such as the get_ptc helper, but the warp implementation still needs to be complete as to send the response to the VC

@ethDreamer
@eserilev

@chong-he chong-he added the gloas label Nov 3, 2025
@shane-moore shane-moore changed the base branch from gloas-envelope-processing to gloas-validator December 2, 2025 02:13
@shane-moore shane-moore force-pushed the gloas-vc-ptc-duty branch 3 times, most recently from 6c3fa35 to 807ba02 Compare December 18, 2025 02:19
@ethDreamer
Copy link
Member

Since re-org is possible, we also poll every slot.

I don't believe this is correct. Have a look at get_ptc()

def get_ptc(state: BeaconState, slot: Slot) -> Vector[ValidatorIndex, PTC_SIZE]:
    """
    Get the payload timeliness committee for the given ``slot``.
    """
    epoch = compute_epoch_at_slot(slot)
    seed = hash(get_seed(state, epoch, DOMAIN_PTC_ATTESTER) + uint_to_bytes(slot))
    indices: List[ValidatorIndex] = []
    # Concatenate all committees for this slot in order
    committees_per_slot = get_committee_count_per_slot(state, epoch)
    for i in range(committees_per_slot):
        committee = get_beacon_committee(state, slot, CommitteeIndex(i))
        indices.extend(committee)
    return compute_balance_weighted_selection(
        state, indices, seed, size=PTC_SIZE, shuffle_indices=False
    )

The committee selection appears constant for the same seed, and the seed is constant over MIN_SEED_LOOKAHEAD = 1 epoch

@ethDreamer
Copy link
Member

ohhh you just mean that you check the cache every slot to ensure we have the same dependent root.


type AttesterMap = HashMap<PublicKeyBytes, HashMap<Epoch, (DependentRoot, DutyAndProof)>>;
type ProposerMap = HashMap<Epoch, (DependentRoot, Vec<ProposerData>)>;
type PtcMap = HashMap<Epoch, (DependentRoot, HashMap<PublicKeyBytes, PtcDuty>)>;
Copy link
Member

Choose a reason for hiding this comment

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

If the beacon node only returns duties for pubkeys that this validator custodies, then this should probably just be a

HashMap<Epoch, (DependentRoot, Vec<PtcDuty>)>

as this Vec will almost always be quite small and iterating through it will be fast

@michaelsproul michaelsproul added the val-client Relates to the validator client binary label Jan 20, 2026
@michaelsproul michaelsproul mentioned this pull request Jan 20, 2026
12 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gloas val-client Relates to the validator client binary

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants