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

chore: Log when validator enters commitee #10615

Merged
merged 1 commit into from
Dec 11, 2024
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
2 changes: 1 addition & 1 deletion yarn-project/epoch-cache/src/epoch_cache.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ describe('EpochCache', () => {
expect(nextNextProposer).toEqual(testCommittee[0]);
});

it('Should request to update the validator set when on the epoch boundary', async () => {
it('should request to update the validator set when on the epoch boundary', async () => {
// Set initial time to a known slot
const initialTime = Number(l1GenesisTime) * 1000; // Convert to milliseconds
jest.setSystemTime(initialTime);
Expand Down
13 changes: 10 additions & 3 deletions yarn-project/epoch-cache/src/epoch_cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { RollupContract, createEthereumChain } from '@aztec/ethereum';
import { EthAddress } from '@aztec/foundation/eth-address';
import { type Logger, createLogger } from '@aztec/foundation/log';

import { EventEmitter } from 'node:events';
import { createPublicClient, encodeAbiParameters, http, keccak256 } from 'viem';

import { type EpochCacheConfig, getEpochCacheConfigEnvVars } from './config.js';
Expand All @@ -28,7 +29,7 @@ type EpochAndSlot = {
*
* Note: This class is very dependent on the system clock being in sync.
*/
export class EpochCache {
export class EpochCache extends EventEmitter<{ committeeChanged: [EthAddress[], bigint] }> {
private committee: EthAddress[];
private cachedEpoch: bigint;
private cachedSampleSeed: bigint;
Expand All @@ -40,6 +41,7 @@ export class EpochCache {
initialSampleSeed: bigint = 0n,
private readonly l1constants: L1RollupConstants = EmptyL1RollupConstants,
) {
super();
this.committee = initialValidators;
this.cachedSampleSeed = initialSampleSeed;

Expand Down Expand Up @@ -111,14 +113,19 @@ export class EpochCache {
const { epoch: calculatedEpoch, ts } = nextSlot ? this.getEpochAndSlotInNextSlot() : this.getEpochAndSlotNow();

if (calculatedEpoch !== this.cachedEpoch) {
this.log.debug(`Epoch changed, updating validator set`, { calculatedEpoch, cachedEpoch: this.cachedEpoch });
this.cachedEpoch = calculatedEpoch;
this.log.debug(`Updating validator set for new epoch ${calculatedEpoch}`, {
epoch: calculatedEpoch,
previousEpoch: this.cachedEpoch,
});
const [committeeAtTs, sampleSeedAtTs] = await Promise.all([
this.rollup.getCommitteeAt(ts),
this.rollup.getSampleSeedAt(ts),
]);
this.committee = committeeAtTs.map((v: `0x${string}`) => EthAddress.fromString(v));
this.cachedEpoch = calculatedEpoch;
this.cachedSampleSeed = sampleSeedAtTs;
this.log.debug(`Updated validator set for epoch ${calculatedEpoch}`, { commitee: this.committee });
this.emit('committeeChanged', this.committee, calculatedEpoch);
}

return this.committee;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export class SequencerClient {
telemetryClient,
config,
);

await validatorClient?.start();
await sequencer.start();
return new SequencerClient(sequencer);
}
Expand Down
1 change: 1 addition & 0 deletions yarn-project/sequencer-client/src/sequencer/sequencer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ export class Sequencer {
*/
public async stop(): Promise<void> {
this.log.debug(`Stopping sequencer`);
await this.validatorClient?.stop();
await this.runningPromise?.stop();
this.publisher.interrupt();
this.setState(SequencerState.STOPPED, 0n, true /** force */);
Expand Down
36 changes: 33 additions & 3 deletions yarn-project/validator-client/src/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { type EpochCache } from '@aztec/epoch-cache';
import { Buffer32 } from '@aztec/foundation/buffer';
import { type Fr } from '@aztec/foundation/fields';
import { createLogger } from '@aztec/foundation/log';
import { RunningPromise } from '@aztec/foundation/running-promise';
import { sleep } from '@aztec/foundation/sleep';
import { type Timer } from '@aztec/foundation/timer';
import { type P2P } from '@aztec/p2p';
Expand Down Expand Up @@ -68,6 +69,8 @@ export class ValidatorClient extends WithTracer implements Validator {
// Callback registered to: sequencer.buildBlock
private blockBuilder?: BlockBuilderCallback = undefined;

private epochCacheUpdateLoop: RunningPromise;

constructor(
private keyStore: ValidatorKeyStore,
private epochCache: EpochCache,
Expand All @@ -81,7 +84,23 @@ export class ValidatorClient extends WithTracer implements Validator {
this.metrics = new ValidatorMetrics(telemetry);

this.validationService = new ValidationService(keyStore);
this.log.verbose('Initialized validator');

// Refresh epoch cache every second to trigger commiteeChanged event
this.epochCacheUpdateLoop = new RunningPromise(async () => {
await this.epochCache.getCommittee().catch(err => log.error('Error updating validator committee', err));
}, 1000);

// Listen to commiteeChanged event to alert operator when their validator has entered the committee
this.epochCache.on('committeeChanged', (newCommittee, epochNumber) => {
const me = this.keyStore.getAddress();
if (newCommittee.some(addr => addr.equals(me))) {
this.log.info(`Validator ${me.toString()} is on the validator committee for epoch ${epochNumber}`);
} else {
this.log.verbose(`Validator ${me.toString()} not on the validator committee for epoch ${epochNumber}`);
}
});

this.log.verbose(`Initialized validator with address ${this.keyStore.getAddress().toString()}`);
}

static new(
Expand All @@ -102,14 +121,25 @@ export class ValidatorClient extends WithTracer implements Validator {
return validator;
}

public start() {
public async start() {
// Sync the committee from the smart contract
// https://github.com/AztecProtocol/aztec-packages/issues/7962

this.log.info('Validator started');
const me = this.keyStore.getAddress();
const inCommittee = await this.epochCache.isInCommittee(me);
if (inCommittee) {
this.log.info(`Started validator with address ${me.toString()} in current validator committee`);
} else {
this.log.info(`Started validator with address ${me.toString()}`);
}
this.epochCacheUpdateLoop.start();
return Promise.resolve();
}

public async stop() {
await this.epochCacheUpdateLoop.stop();
}

public registerBlockProposalHandler() {
const handler = (block: BlockProposal): Promise<BlockAttestation | undefined> => {
return this.attestToProposal(block);
Expand Down
Loading