Skip to content
Merged
Show file tree
Hide file tree
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
264 changes: 263 additions & 1 deletion spartan/metrics/grafana/dashboards/aztec_validators.json
Original file line number Diff line number Diff line change
Expand Up @@ -2602,6 +2602,268 @@
],
"title": "Archiver Database Item Count",
"type": "timeseries"
},
{
"collapsed": false,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 88
},
"id": 700,
"panels": [],
"title": "Attester Epoch Participation",
"type": "row"
},
{
"datasource": {
"default": true,
"type": "prometheus",
"uid": "${data_source}"
},
"description": "The current epoch number, which represents the total number of epochs elapsed since genesis.",
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"decimals": 0,
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "blue",
"value": null
}
]
},
"unit": "none"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 4,
"x": 0,
"y": 89
},
"id": 701,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "center",
"orientation": "auto",
"percentChangeColorMode": "standard",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"showPercentChange": false,
"textMode": "auto",
"wideLayout": true
},
"pluginVersion": "11.2.1",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${data_source}"
},
"editorMode": "code",
"expr": "max(aztec_validator_current_epoch{k8s_namespace_name=\"$namespace\", service_instance_id=~\"$service_instance\"})",
"instant": true,
"legendFormat": "Current Epoch",
"range": false,
"refId": "A"
}
],
"title": "Current Epoch",
"type": "stat"
},
{
"datasource": {
"default": true,
"type": "prometheus",
"uid": "${data_source}"
},
"description": "Cumulative number of epochs in which each attester successfully submitted at least one attestation.",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "Epochs attested",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "stepAfter",
"lineWidth": 2,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"decimals": 0,
"mappings": [],
"min": 0,
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 20,
"x": 4,
"y": 89
},
"id": 702,
"options": {
"legend": {
"calcs": [
"lastNotNull"
],
"displayMode": "table",
"placement": "right",
"showLegend": true
},
"tooltip": {
"mode": "multi",
"sort": "desc"
}
},
"pluginVersion": "11.2.1",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${data_source}"
},
"editorMode": "code",
"expr": "aztec_validator_attested_epoch_count{k8s_namespace_name=\"$namespace\", service_instance_id=~\"$service_instance\"}",
"legendFormat": "{{k8s_pod_name}} / {{aztec_attester_address}}",
"range": true,
"refId": "A"
}
],
"title": "Attested Epochs per Attester",
"type": "timeseries"
},
{
"datasource": {
"default": true,
"type": "prometheus",
"uid": "${data_source}"
},
"description": "Fraction of total epochs in which each attester successfully participated (attested epochs ÷ current epoch × 100%).",
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"decimals": 1,
"mappings": [],
"max": 100,
"min": 0,
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "red",
"value": null
},
{
"color": "orange",
"value": 50
},
{
"color": "yellow",
"value": 75
},
{
"color": "green",
"value": 90
}
]
},
"unit": "percent"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 24,
"x": 0,
"y": 97
},
"id": 703,
"options": {
"displayMode": "gradient",
"minVizHeight": 10,
"minVizWidth": 0,
"namePlacement": "auto",
"orientation": "horizontal",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"showUnfilled": true,
"valueMode": "color"
},
"pluginVersion": "11.2.1",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${data_source}"
},
"editorMode": "code",
"expr": "aztec_validator_attested_epoch_count{k8s_namespace_name=\"$namespace\", service_instance_id=~\"$service_instance\"} / on() group_left() max by() (aztec_validator_current_epoch{k8s_namespace_name=\"$namespace\", service_instance_id=~\"$service_instance\"}) * 100",
"instant": true,
"legendFormat": "{{k8s_pod_name}} / {{aztec_attester_address}}",
"range": false,
"refId": "A"
}
],
"title": "Attester Participation Rate",
"type": "bargauge"
}
],
"refresh": "30s",
Expand Down Expand Up @@ -2706,6 +2968,6 @@
"timezone": "",
"title": "Validator node overview",
"uid": "aztec-validators",
"version": 8,
"version": 9,
"weekStart": ""
}
3 changes: 3 additions & 0 deletions yarn-project/telemetry-client/src/attributes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,6 @@ export const L1_BLOCK_PROPOSAL_TX_TARGET = 'aztec.l1.block_proposal_tx_target';

/** Whether tracing methods were used to extract block proposal data */
export const L1_BLOCK_PROPOSAL_USED_TRACE = 'aztec.l1.block_proposal_used_trace';

/** The address of an attester (validator) participating in consensus */
export const ATTESTER_ADDRESS = 'aztec.attester.address';
10 changes: 10 additions & 0 deletions yarn-project/telemetry-client/src/metrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1254,6 +1254,16 @@ export const VALIDATOR_ATTESTATION_FAILED_NODE_ISSUE_COUNT: MetricDefinition = {
description: 'The number of failed attestations due to node issues (timeout, missing data, etc.)',
valueType: ValueType.INT,
};
export const VALIDATOR_CURRENT_EPOCH: MetricDefinition = {
name: 'aztec.validator.current_epoch',
description: 'The current epoch number, reflecting total epochs elapsed since genesis',
valueType: ValueType.INT,
};
export const VALIDATOR_ATTESTED_EPOCH_COUNT: MetricDefinition = {
name: 'aztec.validator.attested_epoch_count',
description: 'The number of epochs in which this node successfully submitted at least one attestation',
valueType: ValueType.INT,
};

export const NODEJS_EVENT_LOOP_DELAY_MIN: MetricDefinition = {
name: 'nodejs.eventloop.delay.min',
Expand Down
18 changes: 18 additions & 0 deletions yarn-project/validator-client/src/metrics.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { EpochNumber } from '@aztec/foundation/branded-types';
import type { EthAddress } from '@aztec/foundation/eth-address';
import type { BlockProposal } from '@aztec/stdlib/p2p';
import {
Attributes,
Expand All @@ -16,6 +18,8 @@ export class ValidatorMetrics {
private successfulAttestationsCount: UpDownCounter;
private failedAttestationsBadProposalCount: UpDownCounter;
private failedAttestationsNodeIssueCount: UpDownCounter;
private currentEpoch: Gauge;
private attestedEpochCount: UpDownCounter;

private reexMana: Histogram;
private reexTx: Histogram;
Expand Down Expand Up @@ -64,6 +68,10 @@ export class ValidatorMetrics {
},
);

this.currentEpoch = meter.createGauge(Metrics.VALIDATOR_CURRENT_EPOCH);

this.attestedEpochCount = createUpDownCounterWithDefault(meter, Metrics.VALIDATOR_ATTESTED_EPOCH_COUNT);

this.reexMana = meter.createHistogram(Metrics.VALIDATOR_RE_EXECUTION_MANA);

this.reexTx = meter.createHistogram(Metrics.VALIDATOR_RE_EXECUTION_TX_COUNT);
Expand Down Expand Up @@ -110,4 +118,14 @@ export class ValidatorMetrics {
[Attributes.IS_COMMITTEE_MEMBER]: inCommittee,
});
}

/** Update the gauge tracking the current epoch number (proxy for total epochs elapsed). */
public setCurrentEpoch(epoch: EpochNumber) {
this.currentEpoch.record(Number(epoch));
}

/** Increment the count of epochs in which the given attester submitted at least one attestation. */
public incAttestedEpochCount(attester: EthAddress) {
this.attestedEpochCount.add(1, { [Attributes.ATTESTER_ADDRESS]: attester.toString() });
}
}
14 changes: 14 additions & 0 deletions yarn-project/validator-client/src/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)

private lastEpochForCommitteeUpdateLoop: EpochNumber | undefined;
private epochCacheUpdateLoop: RunningPromise;
/** Tracks the last epoch in which each attester successfully submitted at least one attestation. */
private lastAttestedEpochByAttester: Map<string, EpochNumber> = new Map();

private proposersOfInvalidBlocks: Set<string> = new Set();

Expand Down Expand Up @@ -160,6 +162,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)
this.log.trace(`No committee found for slot`);
return;
}
this.metrics.setCurrentEpoch(epoch);
if (epoch !== this.lastEpochForCommitteeUpdateLoop) {
const me = this.getValidatorAddresses();
const committeeSet = new Set(committee.map(v => v.toString()));
Expand Down Expand Up @@ -556,6 +559,17 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter)

this.metrics.incSuccessfulAttestations(inCommittee.length);

// Track epoch participation per attester: count each (attester, epoch) pair at most once
const proposalEpoch = getEpochAtSlot(slotNumber, this.epochCache.getL1Constants());
for (const attester of inCommittee) {
const key = attester.toString();
const lastEpoch = this.lastAttestedEpochByAttester.get(key);
if (lastEpoch === undefined || proposalEpoch > lastEpoch) {
this.lastAttestedEpochByAttester.set(key, proposalEpoch);
this.metrics.incAttestedEpochCount(attester);
}
}

// Determine which validators should attest
let attestors: EthAddress[];
if (partOfCommittee) {
Expand Down
Loading