Skip to content

3.4 Code Lodestar Attestation

Receive Gossip Topic beacon_attestation_{subnet_id}#

In https://github.com/ChainSafe/lodestar/blob/v1.5.1/packages/beacon-node/src/network/gossip/handlers/index.ts L:227

export function getGossipHandlers (modules: ValidatorFnsModules, options: GossipHandlerOpts): GossipHandlers {
  const {chain, config, metrics, network, logger} = modules;
  return {
    [GossipType.beacon_attestation]: async (attestation, {subnet}, _peer, seenTimestampSec) => {
      let validationResult: {indexedAttestation: phase0.IndexedAttestation; subnet: number};
      try {
        /* calls validateGossipAttestation. will go into details later. */
        validationResult = await validateGossipAttestationRetryUnknownRoot(chain, attestation, subnet);
      } catch (e) {
        if (e instanceof AttestationError && e.action === GossipAction.REJECT) {
          chain.persistInvalidSszValue(ssz.phase0.Attestation, attestation, "gossip_reject");
        }
        throw e;
      }

      // Handler
      const {indexedAttestation} = validationResult;
      metrics?.registerGossipUnaggregatedAttestation(seenTimestampSec, indexedAttestation);

      // Node may be subscribe to extra subnets (long-lived random subnets). For those, validate the messages
      // but don't import them, to save CPU and RAM
      if (!network.attnetsService.shouldProcess(subnet, attestation.data.slot)) {
        return;
      }

      try {
        /* add to attestationPool, which later produces aggregated attestation */
        const insertOutcome = chain.attestationPool.add(attestation);
        metrics?.opPool.attestationPoolInsertOutcome.inc({insertOutcome});
      } catch (e) {
        logger.error("Error adding unaggregated attestation to pool", {subnet}, e as Error);
      }

      if (!options.dontSendGossipAttestationsToForkchoice) {
        try {
          chain.forkChoice.onAttestation(indexedAttestation);
        } catch (e) {
          logger.debug("Error adding gossip unaggregated attestation to forkchoice", {subnet}, e as Error);
        }
      }
    },
  }
}

publishBeaconAttestation packages/beacon-node/src/network/gossip/gossipsub.ts ->
submitPoolAttestations packages/beacon-node/src/api/impl/beacon/pool/index.ts ->
signAndPublishAttestations packages/validator/src/services/attestation.ts

Send Gossip Topic beacon_attestation_{subnet_id}#

produceAttestation is called every slot by AttestationService if the validator is 'on duty' to be an attester of the committee.

const attestationNoCommittee = await this.produceAttestation(duties[0].duty.committeeIndex, slot);

It further calls api.validator.produceAttestationData(), which depends on the data from the chain.forkChoice and chain.getHeadState. (refer to 3.5 Code - Lodestar - Block Building#importBlock)

await this.signAndPublishAttestations(slot, attestationNoCommittee, duties);

It calls api.beacon.submitPoolAttestations(signedAttestations) to send gossip topic beacon_attestation_{subnet_id}

Note that it happens latest in ⅓ of the slot or as soon as the local beacon chain head changes:

await Promise.race([sleep(this.clock.msToSlot(slot + 1 / 3), signal), this.emitter.waitForBlockSlot(slot)]);

Send Gossip Topic beacon_aggregate_and_proof#

produceAndPublishAggregates is called every slot by AttestationService if the validator is 'on duty' to be an aggregator.
It produces aggregated attestation and signing them, and eventually calls the api.validator.publishAggregateAndProofs. It then sends to the global gossip topic GossipType.beacon_aggregate_and_proof.

In https://github.com/ChainSafe/lodestar/blob/v1.5.1/packages/beacon-node/src/chain/opPools/attestationPool.ts function aggregateAttestationInto:

  aggregate.aggregationBits.set(bitIndex, true);
  aggregate.signature = bls.Signature.aggregate([
    aggregate.signature,
    signatureFromBytesNoCheck(attestation.signature),
  ]);

It aggregates single attestations and their signatures using the BLS curve. For a great explanation of attestation and BLS curve, refer to BLS Signatures from eth2book

Attestation Committee and Aggregator Selection#

(The same principle actually also applies to other committees, like sync committee.)

It is calculated per epoch by a calculation based on the locally known validators and their BLS signature. The selection of aggregator is based on RANDAO and BLS signature. For detailed explanation, refer to:

Some highlights: