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

feat: epoch cache, do not attest if not in committee or from current proposer #10327

Merged
merged 29 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
e2d7829
feat: delete attestations older than a slot
Maddiaa0 Nov 30, 2024
67ea59c
fmt
Maddiaa0 Nov 30, 2024
b95bcef
test: attestation pool pruning
Maddiaa0 Nov 30, 2024
f6ac466
fmt
Maddiaa0 Nov 30, 2024
e67eaef
feat: epoch cache, do not attest if not in committee or from current …
Maddiaa0 Nov 30, 2024
0265e47
cleanup
Maddiaa0 Nov 30, 2024
6a795ea
test: validator client tests
Maddiaa0 Nov 30, 2024
50eaa4c
fmt
Maddiaa0 Nov 30, 2024
98b118a
fmt + test
Maddiaa0 Dec 2, 2024
6507c02
tmp
Maddiaa0 Dec 3, 2024
97f35c3
fix: next and current slot
Maddiaa0 Dec 5, 2024
7003114
fmt
Maddiaa0 Dec 5, 2024
452e781
Merge branch 'master' into md/epoch-cache--
Maddiaa0 Dec 5, 2024
8c8df0c
post merge fix
Maddiaa0 Dec 5, 2024
e2b43fb
fix: ordering
Maddiaa0 Dec 5, 2024
e0976bb
fix: ordering
Maddiaa0 Dec 5, 2024
da87b55
fix
Maddiaa0 Dec 5, 2024
5fc9900
fix: linter and test
just-mitch Dec 5, 2024
17353cf
Merge branch 'master' into md/epoch-cache--
just-mitch Dec 5, 2024
bcbcccd
fix: merge conflicts
just-mitch Dec 5, 2024
1309414
fix: function ordering and p2p tests
just-mitch Dec 5, 2024
6aba551
fix: reex test - sending same proposal twice in one slot
Maddiaa0 Dec 6, 2024
82da16b
fix: for a slot ci runner, decrease the time jump length
Maddiaa0 Dec 6, 2024
13ec8e4
fix: epoch cache flake
Maddiaa0 Dec 6, 2024
676b7ed
fix: in native testnet, deploy validators at genesis
Maddiaa0 Dec 6, 2024
ca99658
fix: run interleaved
Maddiaa0 Dec 6, 2024
e955e8d
fix: earthfile
Maddiaa0 Dec 6, 2024
dd88dbf
Merge branch 'master' into md/epoch-cache--
Maddiaa0 Dec 6, 2024
38f4b15
fix: unused import
just-mitch Dec 6, 2024
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
5 changes: 5 additions & 0 deletions l1-contracts/src/core/Leonidas.sol
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,11 @@ contract Leonidas is Ownable, TimeFns, ILeonidas {
validatorSet.add(_validator);
}

// Public view function for get committee at
function getCommitteeAt(Timestamp _ts) external view returns (address[] memory) {
return _getCommitteeAt(_ts);
}

function _getCommitteeAt(Timestamp _ts) internal view returns (address[] memory) {
Epoch epochNumber = getEpochAt(_ts);
EpochData storage epoch = epochs[epochNumber];
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/accounts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,4 @@
"engines": {
"node": ">=18"
}
}
}
28 changes: 6 additions & 22 deletions yarn-project/archiver/src/archiver/archiver.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import {
EmptyL1RollupConstants,
type GetUnencryptedLogsResponse,
type InBlock,
type InboxLeaf,
type L1RollupConstants,
type L1ToL2MessageSource,
type L2Block,
type L2BlockId,
Expand All @@ -15,6 +17,10 @@ import {
type TxReceipt,
type TxScopedL2Log,
type UnencryptedL2Log,
getEpochNumberAtTimestamp,
getSlotAtTimestamp,
getSlotRangeForEpoch,
getTimestampRangeForEpoch,
} from '@aztec/circuit-types';
import {
type ContractClassPublic,
Expand Down Expand Up @@ -62,12 +68,6 @@ import {
import { type ArchiverDataStore, type ArchiverL1SynchPoint } from './archiver_store.js';
import { type ArchiverConfig } from './config.js';
import { retrieveBlockFromRollup, retrieveL1ToL2Messages } from './data_retrieval.js';
import {
getEpochNumberAtTimestamp,
getSlotAtTimestamp,
getSlotRangeForEpoch,
getTimestampRangeForEpoch,
} from './epoch_helpers.js';
import { ArchiverInstrumentation } from './instrumentation.js';
import { type DataRetrieval } from './structs/data_retrieval.js';
import { type L1Published } from './structs/published.js';
Expand Down Expand Up @@ -1061,19 +1061,3 @@ class ArchiverStoreHelper
return this.store.estimateSize();
}
}

type L1RollupConstants = {
l1StartBlock: bigint;
l1GenesisTime: bigint;
slotDuration: number;
epochDuration: number;
ethereumSlotDuration: number;
};

const EmptyL1RollupConstants: L1RollupConstants = {
l1StartBlock: 0n,
l1GenesisTime: 0n,
epochDuration: 0,
slotDuration: 0,
ethereumSlotDuration: 0,
};
3 changes: 1 addition & 2 deletions yarn-project/archiver/src/test/mock_l2_block_source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@ import {
TxReceipt,
TxStatus,
} from '@aztec/circuit-types';
import { getSlotRangeForEpoch } from '@aztec/circuit-types';
import { EthAddress, type Header } from '@aztec/circuits.js';
import { DefaultL1ContractsConfig } from '@aztec/ethereum';
import { createDebugLogger } from '@aztec/foundation/log';

import { getSlotRangeForEpoch } from '../archiver/epoch_helpers.js';

/**
* A mocked implementation of L2BlockSource to be used in tests.
*/
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/aztec-node/src/aztec-node/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ export class AztecNodeService implements AztecNode {
// start both and wait for them to sync from the block source
await Promise.all([p2pClient.start(), worldStateSynchronizer.start()]);

const validatorClient = createValidatorClient(config, p2pClient, telemetry);
const validatorClient = await createValidatorClient(config, config.l1Contracts.rollupAddress, p2pClient, telemetry);

// now create the sequencer
const sequencer = config.disableValidator
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
// REFACTOR: This file should go in a package lower in the dependency graph.
export type L1RollupConstants = {
l1StartBlock: bigint;
l1GenesisTime: bigint;
slotDuration: number;
epochDuration: number;
ethereumSlotDuration: number;
};

export const EmptyL1RollupConstants: L1RollupConstants = {
l1StartBlock: 0n,
l1GenesisTime: 0n,
epochDuration: 0,
slotDuration: 0,
ethereumSlotDuration: 0,
};

export type EpochConstants = {
l1GenesisBlock: bigint;
Expand Down
1 change: 1 addition & 0 deletions yarn-project/circuit-types/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ export * from './tx_execution_request.js';
export * from './in_block.js';
export * from './nullifier_with_block_source.js';
export * from './proving_error.js';
export * from './epoch-helpers/index.js';
2 changes: 1 addition & 1 deletion yarn-project/circuits.js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,4 @@
]
]
}
}
}
1 change: 1 addition & 0 deletions yarn-project/epoch-cache/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('@aztec/foundation/eslint');
3 changes: 3 additions & 0 deletions yarn-project/epoch-cache/READMD.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## Epoch Cache

Stores the current validator set.
93 changes: 93 additions & 0 deletions yarn-project/epoch-cache/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
{
"name": "@aztec/epoch-cache",
"version": "0.1.0",
"type": "module",
"exports": {
".": "./dest/index.js",
"./test": "./dest/test/index.js",
"./contracts": "./dest/contracts/index.js"
},
"typedocOptions": {
"entryPoints": [
"./src/index.ts"
],
"name": "Epoch Cache",
"tsconfig": "./tsconfig.json"
},
"scripts": {
"build": "yarn clean && tsc -b",
"build:dev": "tsc -b --watch",
"clean": "rm -rf ./dest .tsbuildinfo",
"formatting": "run -T prettier --check ./src && run -T eslint ./src",
"formatting:fix": "run -T eslint --fix ./src && run -T prettier -w ./src",
"start:dev": "tsc-watch -p tsconfig.json --onSuccess 'yarn start'",
"start": "node ./dest/index.js",
"test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules ../node_modules/.bin/jest --passWithNoTests"
},
"inherits": [
"../package.common.json"
],
"dependencies": {
"@aztec/circuit-types": "workspace:*",
"@aztec/ethereum": "workspace:*",
"@aztec/foundation": "workspace:^",
"@aztec/l1-artifacts": "workspace:^",
"@viem/anvil": "^0.0.10",
"dotenv": "^16.0.3",
"get-port": "^7.1.0",
"tslib": "^2.4.0",
"viem": "^2.7.15",
"zod": "^3.23.8"
},
"devDependencies": {
"@jest/globals": "^29.5.0",
"@types/jest": "^29.5.0",
"@types/node": "^18.14.6",
"jest": "^29.5.0",
"ts-node": "^10.9.1",
"typescript": "^5.0.4"
},
"files": [
"dest",
"src",
"!*.test.*"
],
"types": "./dest/index.d.ts",
"jest": {
"moduleNameMapper": {
"^(\\.{1,2}/.*)\\.[cm]?js$": "$1"
},
"testRegex": "./src/.*\\.test\\.(js|mjs|ts)$",
"rootDir": "./src",
"transform": {
"^.+\\.tsx?$": [
"@swc/jest",
{
"jsc": {
"parser": {
"syntax": "typescript",
"decorators": true
},
"transform": {
"decoratorVersion": "2022-03"
}
}
}
]
},
"extensionsToTreatAsEsm": [
".ts"
],
"reporters": [
[
"default",
{
"summaryThreshold": 9999
}
]
]
},
"engines": {
"node": ">=18"
}
}
10 changes: 10 additions & 0 deletions yarn-project/epoch-cache/src/epoch_cache.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// import { EpochCache } from "./epoch_cache.js";

describe('EpochCache', () => {
it('Should return the current validator', () => {
expect(true).toBe(true);
// const epochCache = await EpochCache.create(EthAddress.random());
// const currentValidator = await epochCache.getCurrentValidator();
// expect(currentValidator).toBeDefined();
});
});
104 changes: 104 additions & 0 deletions yarn-project/epoch-cache/src/epoch_cache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import {
EmptyL1RollupConstants,
type L1RollupConstants,
getEpochNumberAtTimestamp,
getSlotAtTimestamp,
} from '@aztec/circuit-types';
import {
RollupContract,
createEthereumChain,
getL1ContractsConfigEnvVars,
getL1ReaderConfigFromEnv,
} from '@aztec/ethereum';
import { EthAddress } from '@aztec/foundation/eth-address';

import { createPublicClient, http } from 'viem';

type EpochAndSlot = {
epoch: bigint;
slot: bigint;
ts: bigint;
};

export class EpochCache {
private validators: Map<EthAddress, boolean>;
private currentEpoch: bigint;

constructor(
private rollup: RollupContract,
private readonly l1constants: L1RollupConstants = EmptyL1RollupConstants,
) {
this.validators = new Map<EthAddress, boolean>();

this.currentEpoch = getEpochNumberAtTimestamp(BigInt(Math.floor(Date.now() / 1000)), this.l1constants);
}

// TODO: cleanup and merge rollup getters with l1 createAndSync in the archiver
static async create(rollupAddress: EthAddress) {
const l1ReaderConfig = getL1ReaderConfigFromEnv();
const l1constants = getL1ContractsConfigEnvVars();

const chain = createEthereumChain(l1ReaderConfig.l1RpcUrl, l1ReaderConfig.l1ChainId);
const publicClient = createPublicClient({
chain: chain.chainInfo,
transport: http(chain.rpcUrl),
pollingInterval: l1ReaderConfig.viemPollingIntervalMS,
});

const rollup = new RollupContract(publicClient, rollupAddress.toString());
const [l1StartBlock, l1GenesisTime] = await Promise.all([
rollup.getL1StartBlock(),
rollup.getL1GenesisTime(),
] as const);

const l1RollupConstants: L1RollupConstants = {
l1StartBlock,
l1GenesisTime,
slotDuration: l1constants.aztecSlotDuration,
epochDuration: l1constants.aztecEpochDuration,
ethereumSlotDuration: l1constants.ethereumSlotDuration,
};

return new EpochCache(rollup, l1RollupConstants);
}

getEpochAndSlotNow(): EpochAndSlot {
const now = BigInt(Math.floor(Date.now() / 1000));
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit worried that clock drift will cause issues in this setup, but do not want to poll the smart contract for everything, what do we think?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. We could have it reset at a preconfigured interval? e.g. once per epoch sync with the rollup.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filed #10435

return this.getEpochAndSlotAtTimestamp(now);
}

getEpochAndSlotAtTimestamp(ts: bigint): EpochAndSlot {
return {
epoch: getEpochNumberAtTimestamp(ts, this.l1constants),
slot: getSlotAtTimestamp(ts, this.l1constants),
ts,
};
}

async getValidatorSet(): Promise<EthAddress[]> {
// If the current epoch has changed, then we need to make a request to update the validator set
const { epoch: currentEpoch, ts } = this.getEpochAndSlotNow();

if (currentEpoch !== this.currentEpoch) {
this.currentEpoch = currentEpoch;
const validatorSet = await this.rollup.getCommitteeAt(ts);
this.validators = new Map(validatorSet.map((v: `0x${string}`) => [EthAddress.fromString(v), true]));
}

return Array.from(this.validators.keys());
}

async getCurrentValidator(): Promise<EthAddress> {
// Validators are sorted by their index in the committee, and getValidatorSet will cache
// TODO: should we get the timestamp from the underlying l1 node?
const { slot: currentSlot } = this.getEpochAndSlotNow();
const validators = await this.getValidatorSet();

return validators[Number(currentSlot) % validators.length];
}

async isInCommittee(validator: EthAddress): Promise<boolean> {
const validators = await this.getValidatorSet();
return validators.includes(validator);
}
}
1 change: 1 addition & 0 deletions yarn-project/epoch-cache/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './epoch_cache.js';
23 changes: 23 additions & 0 deletions yarn-project/epoch-cache/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"extends": "..",
"compilerOptions": {
"outDir": "dest",
"rootDir": "src",
"tsBuildInfoFile": ".tsbuildinfo"
},
"include": ["src"],
"references": [
{
"path": "../circuit-types"
},
{
"path": "../ethereum"
},
{
"path": "../foundation"
},
{
"path": "../l1-artifacts"
}
]
}
4 changes: 4 additions & 0 deletions yarn-project/ethereum/src/contracts/rollup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ export class RollupContract {
return this.rollup.read.getCurrentSlot();
}

async getCommitteeAt(timestamp: bigint) {
return this.rollup.read.getCommitteeAt([timestamp]);
}

async getEpochNumber(blockNumber?: bigint) {
blockNumber ??= await this.getBlockNumber();
return this.rollup.read.getEpochForBlock([BigInt(blockNumber)]);
Expand Down
1 change: 1 addition & 0 deletions yarn-project/ethereum/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export * from './ethereum_chain.js';
export * from './utils.js';
export * from './config.js';
export * from './types.js';
export * from './contracts/index.js';
Loading
Loading