Skip to content

adding grpc endpoints for attester, proposer, sync duties, and ptc duties - #16416

Merged
james-prysm merged 17 commits into
developfrom
grpc-split-duties-apis
Mar 13, 2026
Merged

adding grpc endpoints for attester, proposer, sync duties, and ptc duties#16416
james-prysm merged 17 commits into
developfrom
grpc-split-duties-apis

Conversation

@james-prysm

@james-prysm james-prysm commented Feb 26, 2026

Copy link
Copy Markdown
Contributor

What type of PR is this?

Other

What does this PR do? Why is it needed?

DEPENDS ON #16402

Adding grpc endpoints for attester, proposer, and sync duties. This pr doesn't utilize the apis.
In a future pr we will include a transition from using duties v2 endpoint to the split duties endpoints for the fork.

TESTING

Both the GetDutiesV2 endpoint and the new ones added ( GetAttesterDuties, GetProposerDutiesV2, GetSyncCommitteeDuties, GetPTCDuties) use the same underlying helper function under coreservice.duty function, so all core duties should be the same. Note that the new gRPC endpoints use validator indices instead of pubkeys to match REST api endpoints.

Using grpcurl (install with brew install grpcurl):

  # GetDutiesV2 - composite endpoint
  # Note: public_keys are base64-encoded BLS pubkeys
  grpcurl -plaintext -d '{
    "epoch": '$EPOCH',
    "public_keys": ["<base64_pubkey_1>", "<base64_pubkey_2>"]
  }' localhost:4000
  ethereum.eth.v1alpha1.BeaconNodeValidator/GetDutiesV2
  # GetAttesterDuties
  grpcurl -plaintext -d '{
    "epoch": '$EPOCH',
    "validator_indices": [0, 1, 2, 3, 4]
  }' localhost:4000
  ethereum.eth.v1alpha1.BeaconNodeValidator/GetAttesterDuties
  # GetProposerDutiesV2
  grpcurl -plaintext -d '{
    "epoch": '$EPOCH'
  }' localhost:4000
  ethereum.eth.v1alpha1.BeaconNodeValidator/GetProposerDutiesV2
  # GetSyncCommitteeDuties
  grpcurl -plaintext -d '{
    "epoch": '$EPOCH',
    "validator_indices": [0, 1, 2, 3, 4]
  }' localhost:4000 ethereum.eth.v1alpha1.BeaconNodeValidator/Get
  SyncCommitteeDuties
  # GetPTCDuties (Gloas+ only)
  grpcurl -plaintext -d '{
    "epoch": '$EPOCH',
    "validator_indices": [0, 1, 2, 3, 4]
  }' localhost:4000
  ethereum.eth.v1alpha1.BeaconNodeValidator/GetPTCDuties

Verification Checklist

  1. Attester duty data match: For each validator in
    GetDutiesV2.CurrentEpochDuties, confirm attester_slot,
    committee_index, committee_length, committees_at_slot,
    validator_committee_index match the corresponding AttesterDuty
    from GetAttesterDuties

  2. Proposer duty data match: For each validator with non-empty
    proposer_slots in GetDutiesV2, confirm those slots appear in
    GetProposerDutiesV2 response for that validator index

  3. Sync committee flag match: For each validator where
    is_sync_committee=true in GetDutiesV2, confirm that validator
    appears in GetSyncCommitteeDuties response

  4. PTC duty data match: For each validator with non-empty
    ptc_slots in GetDutiesV2, confirm those slots appear in
    GetPTCDuties response for that validator index

  5. Dependent root — attester:
    GetDutiesV2.PreviousDutyDependentRoot ==
    GetAttesterDuties.dependent_root (for the same epoch)

  6. Dependent root — proposer pre-Fulu:
    GetDutiesV2.CurrentDutyDependentRoot ==
    GetProposerDutiesV2.dependent_root

  7. Dependent root — proposer post-Fulu: ( DutiesV2 still has a bug that doesn't take into account the proposer look ahead)
    GetDutiesV2.CurrentDutyDependentRoot ≠
    GetProposerDutiesV2.dependent_root. The V2 value should match
    GetAttesterDuties.dependent_root instead (both use (E-1)_start - 1)

  8. Dependent root — PTC: GetPTCDuties.dependent_root ==
    GetAttesterDuties.dependent_root (both use
    AttestationDependentRoot)

  9. Next epoch: Repeat checks 1-4 using
    GetDutiesV2.NextEpochDuties vs individual endpoints queried
    with epoch+1

  10. Edge cases: Epoch 0, epoch 1, sync committee period
    boundary, pre-Gloas epoch for PTC (should error)

Which issues(s) does this PR fix?

Fixes #

Other notes for review

Acknowledgements

  • I have read CONTRIBUTING.md.
  • I have included a uniquely named changelog fragment file.
  • I have added a description with sufficient context for reviewers to understand this PR.
  • I have tested that my changes work as expected and I added a testing plan to the PR description (if applicable).

@james-prysm james-prysm added blocked api cleanup PR improving code health labels Feb 26, 2026
Comment thread testing/validator-mock/validator_client_mock.go Fixed
@james-prysm
james-prysm force-pushed the grpc-split-duties-apis branch from 34d4ec0 to 455f3d5 Compare February 26, 2026 22:04
return nil
}

func (c *beaconApiValidatorClient) AttesterDuties(ctx context.Context, epoch primitives.Epoch, validatorIndices []primitives.ValidatorIndex) (*ethpb.AttesterDutiesResponse, error) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

THis currently doesn't have any tests as the call itself is just an API call, calling the endpoint is tested on the endpoint itself

}, nil
}

func (c *grpcValidatorClient) AttesterDuties(ctx context.Context, epoch primitives.Epoch, validatorIndices []primitives.ValidatorIndex) (*ethpb.AttesterDutiesResponse, error) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

same reason of not having tests as beacon-api folder

}

// Deprecated: Use the REST API instead. GetProposerDutiesV2 retrieves proposer duties for the given epoch.
rpc GetProposerDutiesV2(ProposerDutiesRequest) returns (ProposerDutiesResponse) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I called this V2 to match the beacon api one

@james-prysm
james-prysm marked this pull request as ready for review March 3, 2026 22:22
@james-prysm james-prysm changed the title adding grpc endpoints for attester, proposer, and sync duties adding grpc endpoints for attester, proposer, sync duties, and ptc duties Mar 9, 2026
github-merge-queue Bot pushed a commit that referenced this pull request Mar 9, 2026
**What type of PR is this?**

Other

**What does this PR do? Why is it needed?**

This PR refactors the way we store different validator duties into a
duties store for easier splitting of tasks and in a future pr processing
duties for split endpoints. This PR will reduce the number of changes
when we start calling the different endpoints introduced in
#16416

pr is part of #16421

**Which issues(s) does this PR fix?**

Fixes #

**Other notes for review**

**Acknowledgements**

- [x] I have read
[CONTRIBUTING.md](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md).
- [x] I have included a uniquely named [changelog fragment
file](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md#maintaining-changelogmd).
- [x] I have added a description with sufficient context for reviewers
to understand this PR.
- [x] I have tested that my changes work as expected and I added a
testing plan to the PR description (if applicable).

---------

Co-authored-by: Preston Van Loon <pvanloon@offchainlabs.com>
@james-prysm

Copy link
Copy Markdown
Contributor Author

Note for reviewer ethereum/beacon-APIs#586 might affect this pr regarding ptc duties

}
s, err = vs.stateForEpoch(ctx, s, req.Epoch)
if err != nil {
return nil, err

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why not wrapping the error for this case?

Comment on lines +44 to +56
var dependentRoot []byte
if req.Epoch <= 1 {
r, err := vs.BeaconDB.GenesisBlockRoot(ctx)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get genesis block root: %v", err)
}
dependentRoot = r[:]
} else {
dependentRoot, err = core.AttestationDependentRoot(s, req.Epoch)
if err != nil {
return nil, status.Errorf(codes.Internal, "Could not get dependent root: %v", err)
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It's a matter of taste here, bu the
var dependentRoot []byte and the else could be avoided by defining:

func (vs *Server) dependentRoot(req *ethpb.AttesterDutiesRequest) ([]byte, error) {
	if req.Epoch <= 1 {
		root, err := vs.BeaconDB.GenesisBlockRoot(ctx)
		if err != nil {
			return nil, status.Errorf(codes.Internal, "Could not get genesis block root: %v", err)
		}

        return root[:]
    }

    root, err := core.AttestationDependentRoot(s, req.Epoch)
    if err != nil {
        return nil, status.Errorf(codes.Internal, "Could not get dependent root: %v", err)
    }

    return root
}

And then calling

dependentRoot := dependentRoot(req)

// Pre-Fulu epoch 1 can be computed normally via ProposalDependentRoot.
useGenesisRoot := req.Epoch == 0 || (req.Epoch == 1 && s.Version() >= version.Fulu)
var dependentRoot []byte
if useGenesisRoot {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same for here.

}

var dependentRoot []byte
if req.Epoch <= 1 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same here

}

type ValidatorClient interface {
// Duties is the pre-GLOAS combined endpoint (GetDuties/GetDutiesV2).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Maybe we can write here that this will be eventually replaced.

ptcDuties := structs.GetPTCDutiesResponse{}
if err = c.handler.Post(
ctx,
fmt.Sprintf("/eth/v1/validator/duties/ptc/%d", epoch),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we make it a constant somewhere?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

all the other endpoints are handled in the same way without a constant, if we want to break it out I think we should do it together

syjn99 pushed a commit to syjn99/prysm that referenced this pull request Mar 13, 2026
…s#16479)

**What type of PR is this?**

Other

**What does this PR do? Why is it needed?**

This PR refactors the way we store different validator duties into a
duties store for easier splitting of tasks and in a future pr processing
duties for split endpoints. This PR will reduce the number of changes
when we start calling the different endpoints introduced in
OffchainLabs#16416

pr is part of OffchainLabs#16421

**Which issues(s) does this PR fix?**

Fixes #

**Other notes for review**

**Acknowledgements**

- [x] I have read
[CONTRIBUTING.md](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md).
- [x] I have included a uniquely named [changelog fragment
file](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md#maintaining-changelogmd).
- [x] I have added a description with sufficient context for reviewers
to understand this PR.
- [x] I have tested that my changes work as expected and I added a
testing plan to the PR description (if applicable).

---------

Co-authored-by: Preston Van Loon <pvanloon@offchainlabs.com>
@james-prysm
james-prysm enabled auto-merge March 13, 2026 18:55
@james-prysm
james-prysm added this pull request to the merge queue Mar 13, 2026
Merged via the queue into develop with commit 8680f3f Mar 13, 2026
19 checks passed
@james-prysm
james-prysm deleted the grpc-split-duties-apis branch March 13, 2026 19:44
syjn99 pushed a commit to syjn99/prysm that referenced this pull request Mar 27, 2026
…ties (OffchainLabs#16416)

<!-- Thanks for sending a PR! Before submitting:

1. If this is your first PR, check out our contribution guide here
https://docs.prylabs.network/docs/contribute/contribution-guidelines
You will then need to sign our Contributor License Agreement (CLA),
which will show up as a comment from a bot in this pull request after
you open it. We cannot review code without a signed CLA.
2. Please file an associated tracking issue if this pull request is
non-trivial and requires context for our team to understand. All
features and most bug fixes should have
an associated issue with a design discussed and decided upon. Small bug
   fixes and documentation improvements don't need issues.
3. New features and bug fixes must have tests. Documentation may need to
be updated. If you're unsure what to update, send the PR, and we'll
discuss
   in review.
4. Note that PRs updating dependencies and new Go versions are not
accepted.
   Please file an issue instead.
5. A changelog entry is required for user facing issues.
-->

**What type of PR is this?**

Other

**What does this PR do? Why is it needed?**

~~DEPENDS ON OffchainLabs#16402

Adding grpc endpoints for attester, proposer, and sync duties. This pr
doesn't utilize the apis.
In a future pr we will include a transition from using duties v2
endpoint to the split duties endpoints for the fork.

TESTING

Both the GetDutiesV2 endpoint and the new ones added (
GetAttesterDuties, GetProposerDutiesV2, GetSyncCommitteeDuties,
GetPTCDuties) use the same underlying helper function under
coreservice.duty function, so all core duties should be the same. Note
that the new gRPC endpoints use validator indices instead of pubkeys to
match REST api endpoints.

Using grpcurl (install with brew install grpcurl):
```
  # GetDutiesV2 - composite endpoint
  # Note: public_keys are base64-encoded BLS pubkeys
  grpcurl -plaintext -d '{
    "epoch": '$EPOCH',
    "public_keys": ["<base64_pubkey_1>", "<base64_pubkey_2>"]
  }' localhost:4000
  ethereum.eth.v1alpha1.BeaconNodeValidator/GetDutiesV2
```

```
  # GetAttesterDuties
  grpcurl -plaintext -d '{
    "epoch": '$EPOCH',
    "validator_indices": [0, 1, 2, 3, 4]
  }' localhost:4000
  ethereum.eth.v1alpha1.BeaconNodeValidator/GetAttesterDuties
```

```
  # GetProposerDutiesV2
  grpcurl -plaintext -d '{
    "epoch": '$EPOCH'
  }' localhost:4000
  ethereum.eth.v1alpha1.BeaconNodeValidator/GetProposerDutiesV2
```

```
  # GetSyncCommitteeDuties
  grpcurl -plaintext -d '{
    "epoch": '$EPOCH',
    "validator_indices": [0, 1, 2, 3, 4]
  }' localhost:4000 ethereum.eth.v1alpha1.BeaconNodeValidator/Get
  SyncCommitteeDuties
```

```
  # GetPTCDuties (Gloas+ only)
  grpcurl -plaintext -d '{
    "epoch": '$EPOCH',
    "validator_indices": [0, 1, 2, 3, 4]
  }' localhost:4000
  ethereum.eth.v1alpha1.BeaconNodeValidator/GetPTCDuties
```

  Verification Checklist

  1. Attester duty data match: For each validator in
  GetDutiesV2.CurrentEpochDuties, confirm attester_slot,
  committee_index, committee_length, committees_at_slot,
  validator_committee_index match the corresponding AttesterDuty
  from GetAttesterDuties

  2. Proposer duty data match: For each validator with non-empty
  proposer_slots in GetDutiesV2, confirm those slots appear in
  GetProposerDutiesV2 response for that validator index
  3. Sync committee flag match: For each validator where
  is_sync_committee=true in GetDutiesV2, confirm that validator
  appears in GetSyncCommitteeDuties response

  4. PTC duty data match: For each validator with non-empty
  ptc_slots in GetDutiesV2, confirm those slots appear in
  GetPTCDuties response for that validator index

  5. Dependent root — attester:
  GetDutiesV2.PreviousDutyDependentRoot ==
  GetAttesterDuties.dependent_root (for the same epoch)

  6. Dependent root — proposer pre-Fulu:
  GetDutiesV2.CurrentDutyDependentRoot ==
  GetProposerDutiesV2.dependent_root

7. Dependent root — proposer post-Fulu: ( DutiesV2 still has a bug that
doesn't take into account the proposer look ahead)
  GetDutiesV2.CurrentDutyDependentRoot ≠
  GetProposerDutiesV2.dependent_root. The V2 value should match
  GetAttesterDuties.dependent_root instead (both use (E-1)_start - 1)
  
  8. Dependent root — PTC: GetPTCDuties.dependent_root ==
  GetAttesterDuties.dependent_root (both use
  AttestationDependentRoot)

  9. Next epoch: Repeat checks 1-4 using
  GetDutiesV2.NextEpochDuties vs individual endpoints queried
  with epoch+1

  10. Edge cases: Epoch 0, epoch 1, sync committee period
  boundary, pre-Gloas epoch for PTC (should error)


**Which issues(s) does this PR fix?**

Fixes #

**Other notes for review**

**Acknowledgements**

- [x] I have read
[CONTRIBUTING.md](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md).
- [x] I have included a uniquely named [changelog fragment
file](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md#maintaining-changelogmd).
- [x] I have added a description with sufficient context for reviewers
to understand this PR.
- [x] I have tested that my changes work as expected and I added a
testing plan to the PR description (if applicable).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api cleanup PR improving code health

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants