digraph architecture{
  node [shape = invhouse]; Eth2RPC GossipSub;
  node [shape = octagon]; AttestationPool Eth2Processor DoppelgangerDetection;
  node [shape = doubleoctagon]; AsyncQueueAttestationEntry AsyncQueueAggregateEntry;
  node [shape = octagon]; ForkChoice Quarantine;
  {rank = same; Eth2RPC GossipSub;}

  AsyncQueueAttestationEntry [label="AsyncQueue[AttestationEntry]"];
  AsyncQueueAggregateEntry [label="AsyncQueue[AggregateEntry]"];

  GossipSub -> Eth2Processor [label="getAttestationTopic: attestationValidator->attestationPool.validateAttestation()"];
  GossipSub -> Eth2Processor [label="node.topicAggregateAndProofs: aggregateValidator->attestationPool.validateAggregate()"];

  Eth2Processor -> AttestationAggregation [label="validateAttestation() validateAggregate()"];
  Eth2Processor -> AttestationPool [label="attestation_pool.selectHead()"];
  Eth2Processor -> DoppelgangerDetection

  Eth2Processor -> AsyncQueueAttestationEntry [label="Move to queue after P2P validation via validateAttestation()"];
  Eth2Processor -> AsyncQueueAggregateEntry [label="Move to queue after P2P validation via validateAggregate()"];
  AsyncQueueAttestationEntry -> AttestationPool [label="runQueueProcessingLoop() -> processAttestation()\n->addAttestation (to fork choice)"];
  AsyncQueueAggregateEntry -> AttestationPool [label="runQueueProcessingLoop() -> processAggregate()\n->addAttestation (to fork choice)"];

  AttestationPool -> Quarantine [label="Missing block targets"];
  AttestationPool -> ForkChoice [label="addAttestation(), prune(), selectHead()"];
  Eth2RPC -> AttestationPool [dir="back" label="get_v1_beacon_pool_attestations() -> attestations iterator"]

  LocalValidatorDuties -> AttestationPool [label="Vote for head\nMake a block (with local validators attestations)"];
  LocalValidatorDuties -> AttestationAggregation [label="aggregate_attestations()"];
  AttestationAggregation -> AttestationPool
}