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

Do single call to produce attestation #3917

Merged
merged 1 commit into from
Apr 13, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 30 additions & 12 deletions packages/validator/src/services/attestation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,20 @@ export class AttestationService {
// (b) one-third of the slot has transpired (SECONDS_PER_SLOT / 3 seconds after the start of slot) -- whichever comes first.
await Promise.race([sleep(this.clock.msToSlotFraction(slot, 1 / 3), signal), this.waitForBlockSlot(slot)]);

// Beacon node's endpoint produceAttestationData return data is not dependant on committeeIndex.
// Produce a single attestation for all committees, and clone mutate before signing
const attestationNoCommittee = await this.produceAttestation(slot);

// await for all so if the Beacon node is overloaded it auto-throttles
// TODO: This approach is convervative to reduce the node's load, review
await Promise.all(
Array.from(dutiesByCommitteeIndex.entries()).map(async ([committeeIndex, duties]) => {
if (duties.length === 0) return;
await this.publishAttestationsAndAggregates(slot, committeeIndex, duties, signal).catch((e: Error) => {
this.logger.error("Error on attestations routine", {slot, committeeIndex}, e);
});
await this.publishAttestationsAndAggregates(slot, committeeIndex, duties, attestationNoCommittee, signal).catch(
(e: Error) => {
this.logger.error("Error on attestations routine", {slot, committeeIndex}, e);
}
);
})
);
};
Expand All @@ -91,10 +97,11 @@ export class AttestationService {
slot: Slot,
committeeIndex: CommitteeIndex,
duties: AttDutyAndProof[],
attestationNoCommittee: phase0.AttestationData,
signal: AbortSignal
): Promise<void> {
// Step 1. Download, sign and publish an `Attestation` for each validator.
const attestation = await this.produceAndPublishAttestations(slot, committeeIndex, duties);
// Step 1. Mutate, sign and publish `Attestation` for each validator.
const attestation = await this.publishAttestations(slot, committeeIndex, attestationNoCommittee, duties);

// Step 2. If an attestation was produced, make an aggregate.
// First, wait until the `aggregation_production_instant` (2/3rds of the way though the slot)
Expand All @@ -106,6 +113,20 @@ export class AttestationService {
await this.produceAndPublishAggregates(attestation, duties);
}

/**
* Performs the first step of the attesting process: downloading one `Attestation` object.
* Beacon node's endpoint produceAttestationData return data is not dependant on committeeIndex.
* For a validator client with many validators this allows to do a single call for all committees
* in a slot, saving resources in both the vc and beacon node
*/
private async produceAttestation(slot: Slot): Promise<phase0.AttestationData> {
// Produce one attestation data per slot and committeeIndex
const attestationRes = await this.api.validator.produceAttestationData(0, slot).catch((e: Error) => {
throw extendError(e, "Error producing attestation");
});
return attestationRes.data;
}

/**
* Performs the first step of the attesting process: downloading `Attestation` objects,
* signing them and returning them to the validator.
Expand All @@ -115,22 +136,19 @@ export class AttestationService {
* Only one `Attestation` is downloaded from the BN. It is then signed by each
* validator and the list of individually-signed `Attestation` objects is returned to the BN.
*/
private async produceAndPublishAttestations(
private async publishAttestations(
slot: Slot,
committeeIndex: CommitteeIndex,
attestationNoCommittee: phase0.AttestationData,
duties: AttDutyAndProof[]
): Promise<phase0.AttestationData> {
const logCtx = {slot, index: committeeIndex};

// Produce one attestation data per slot and committeeIndex
const attestationRes = await this.api.validator.produceAttestationData(committeeIndex, slot).catch((e: Error) => {
throw extendError(e, "Error producing attestation");
});
const attestation = attestationRes.data;

const currentEpoch = computeEpochAtSlot(slot);
const signedAttestations: phase0.Attestation[] = [];

const attestation = {...attestationNoCommittee, committeeIndex};

for (const {duty} of duties) {
const logCtxValidator = {
...logCtx,
Expand Down