diff --git a/yarn-project/aztec-node/package.json b/yarn-project/aztec-node/package.json index c06afd94adae..f6c27824a65c 100644 --- a/yarn-project/aztec-node/package.json +++ b/yarn-project/aztec-node/package.json @@ -72,6 +72,7 @@ "@aztec/foundation": "workspace:^", "@aztec/kv-store": "workspace:^", "@aztec/merkle-tree": "workspace:^", + "@aztec/node-lib": "workspace:^", "@aztec/p2p": "workspace:^", "@aztec/protocol-contracts": "workspace:^", "@aztec/prover-client": "workspace:^", diff --git a/yarn-project/aztec-node/src/aztec-node/config.ts b/yarn-project/aztec-node/src/aztec-node/config.ts index 927f62cb6552..e6699178e4ed 100644 --- a/yarn-project/aztec-node/src/aztec-node/config.ts +++ b/yarn-project/aztec-node/src/aztec-node/config.ts @@ -2,6 +2,7 @@ import { type ArchiverConfig, archiverConfigMappings } from '@aztec/archiver/con import { type L1ContractAddresses, l1ContractAddressesMapping } from '@aztec/ethereum'; import { type ConfigMappingsType, booleanConfigHelper, getConfigFromMappings } from '@aztec/foundation/config'; import { type DataStoreConfig, dataConfigMappings } from '@aztec/kv-store/config'; +import { type SharedNodeConfig, sharedNodeConfigMappings } from '@aztec/node-lib/config'; import { type P2PConfig, p2pConfigMappings } from '@aztec/p2p/config'; import { type ProverClientConfig, proverClientConfigMappings } from '@aztec/prover-client/config'; import { type SequencerClientConfig, sequencerClientConfigMappings } from '@aztec/sequencer-client/config'; @@ -27,19 +28,12 @@ export type AztecNodeConfig = ArchiverConfig & Pick & P2PConfig & DataStoreConfig & - SentinelConfig & { - /** Whether the validator is disabled for this node */ - disableValidator: boolean; - /** Whether to populate the genesis state with initial fee juice for the test accounts */ - testAccounts: boolean; - /** Whether to populate the genesis state with initial fee juice for the sponsored FPC */ - sponsoredFPC: boolean; + SentinelConfig & + SharedNodeConfig & { /** L1 contracts addresses */ l1Contracts: L1ContractAddresses; - /** Sync mode: full to always sync via L1, snapshot to download a snapshot if there is no local data, force-snapshot to download even if there is local data. */ - syncMode: 'full' | 'snapshot' | 'force-snapshot'; - /** Base URL for snapshots index. Index file will be searched at `SNAPSHOTS_BASE_URL/aztec-L1_CHAIN_ID-VERSION-ROLLUP_ADDRESS/index.json` */ - snapshotsUrl?: string; + /** Whether the validator is disabled for this node */ + disableValidator: boolean; }; export const aztecNodeConfigMappings: ConfigMappingsType = { @@ -51,6 +45,7 @@ export const aztecNodeConfigMappings: ConfigMappingsType = { ...worldStateConfigMappings, ...p2pConfigMappings, ...sentinelConfigMappings, + ...sharedNodeConfigMappings, l1Contracts: { description: 'The deployed L1 contract addresses', nested: l1ContractAddressesMapping, @@ -60,26 +55,6 @@ export const aztecNodeConfigMappings: ConfigMappingsType = { description: 'Whether the validator is disabled for this node.', ...booleanConfigHelper(), }, - testAccounts: { - env: 'TEST_ACCOUNTS', - description: 'Whether to populate the genesis state with initial fee juice for the test accounts.', - ...booleanConfigHelper(), - }, - sponsoredFPC: { - env: 'SPONSORED_FPC', - description: 'Whether to populate the genesis state with initial fee juice for the sponsored FPC.', - ...booleanConfigHelper(false), - }, - syncMode: { - env: 'SYNC_MODE', - description: - 'Set sync mode to `full` to always sync via L1, `snapshot` to download a snapshot if there is no local data, `force-snapshot` to download even if there is local data.', - defaultValue: 'snapshot', - }, - snapshotsUrl: { - env: 'SYNC_SNAPSHOTS_URL', - description: 'Base URL for snapshots index.', - }, }; /** diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index 472f1a2ad9cf..a2f9d156f662 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -21,6 +21,7 @@ import { SiblingPath } from '@aztec/foundation/trees'; import type { AztecKVStore } from '@aztec/kv-store'; import { openTmpStore } from '@aztec/kv-store/lmdb'; import { SHA256Trunc, StandardTree, UnbalancedTree } from '@aztec/merkle-tree'; +import { trySnapshotSync, uploadSnapshot } from '@aztec/node-lib/actions'; import { type P2P, createP2PClient } from '@aztec/p2p'; import { ProtocolContractAddress } from '@aztec/protocol-contracts'; import { @@ -86,8 +87,6 @@ import { import { createValidatorClient } from '@aztec/validator-client'; import { createWorldStateSynchronizer } from '@aztec/world-state'; -import { trySnapshotSync } from '../actions/snapshot-sync.js'; -import { uploadSnapshot } from '../actions/upload-snapshot.js'; import { createSentinel } from '../sentinel/factory.js'; import { Sentinel } from '../sentinel/sentinel.js'; import { type AztecNodeConfig, getPackageVersion } from './config.js'; @@ -174,7 +173,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable { ); } - // attempt fast sync if needed + // attempt snapshot sync if possible await trySnapshotSync(config, log); const archiver = await createArchiver(config, blobSinkClient, { blockUntilSync: true }, telemetry); diff --git a/yarn-project/aztec-node/tsconfig.json b/yarn-project/aztec-node/tsconfig.json index ad71a8c78ad2..2993d9f26f12 100644 --- a/yarn-project/aztec-node/tsconfig.json +++ b/yarn-project/aztec-node/tsconfig.json @@ -33,6 +33,9 @@ { "path": "../merkle-tree" }, + { + "path": "../node-lib" + }, { "path": "../p2p" }, diff --git a/yarn-project/aztec/src/cli/aztec_start_options.ts b/yarn-project/aztec/src/cli/aztec_start_options.ts index 8cc9abf11e57..e648d8f83a54 100644 --- a/yarn-project/aztec/src/cli/aztec_start_options.ts +++ b/yarn-project/aztec/src/cli/aztec_start_options.ts @@ -282,7 +282,7 @@ export const aztecStartOptions: { [key: string]: AztecStartOption[] } = { }, { flag: '--node.snapshotsUrl ', - description: 'Base URL for downloading snapshots for fast sync.', + description: 'Base URL for downloading snapshots for snapshot sync.', defaultValue: undefined, envVar: 'SYNC_SNAPSHOTS_URL', }, diff --git a/yarn-project/end-to-end/src/e2e_snapshot_sync.test.ts b/yarn-project/end-to-end/src/e2e_snapshot_sync.test.ts index 331869f946c9..599c8681acfa 100644 --- a/yarn-project/end-to-end/src/e2e_snapshot_sync.test.ts +++ b/yarn-project/end-to-end/src/e2e_snapshot_sync.test.ts @@ -5,6 +5,7 @@ import { ChainMonitor } from '@aztec/ethereum/test'; import { randomBytes } from '@aztec/foundation/crypto'; import { tryRmDir } from '@aztec/foundation/fs'; import { withLogNameSuffix } from '@aztec/foundation/log'; +import { type ProverNodeConfig, createProverNode } from '@aztec/prover-node'; import { mkdtemp, readdir } from 'fs/promises'; import { tmpdir } from 'os'; @@ -59,6 +60,28 @@ describe('e2e_snapshot_sync', () => { ); }; + // Adapted from utils/createAndSyncProverNode + const createTestProverNode = async (suffix: string, config: Partial = {}) => { + log.warn('Creating and syncing a prover node...'); + return await withLogNameSuffix(suffix, () => + createProverNode({ + ...context.config, + dataDirectory: join(context.config.dataDirectory!, randomBytes(8).toString('hex')), + p2pEnabled: true, // So we don't need prover coordination + proverCoordinationNodeUrl: undefined, + proverNodeMaxPendingJobs: 10, + proverNodeMaxParallelBlocksPerEpoch: 32, + proverNodePollingIntervalMs: 200, + txGatheringTimeoutMs: 60000, + txGatheringIntervalMs: 1000, + txGatheringMaxParallelRequests: 100, + proverAgentCount: 1, + realProofs: false, + ...config, + }), + ); + }; + it('waits until a few L2 blocks have been mined', async () => { log.warn(`Waiting for L2 blocks to be mined`); await retryUntil(() => monitor.l2BlockNumber > L2_TARGET_BLOCK_NUM, 'l2-blocks-mined', 90, 1); @@ -72,7 +95,7 @@ describe('e2e_snapshot_sync', () => { log.warn(`Snapshot created`); }); - it('downloads snapshot from new node', async () => { + it('downloads snapshot when syncing new node', async () => { log.warn(`Syncing brand new node with snapshot sync`); const node = await createNonValidatorNode('1', { blobSinkUrl: undefined, // set no blob sink so it cannot sync on its own @@ -94,4 +117,23 @@ describe('e2e_snapshot_sync', () => { log.warn(`Stopping new node`); await node.stop(); }); + + it('downloads snapshot when syncing new prover node', async () => { + log.warn(`Syncing brand new prover node with snapshot sync`); + const node = await createTestProverNode('1', { + blobSinkUrl: undefined, // set no blob sink so it cannot sync on its own + snapshotsUrl: snapshotLocation, + syncMode: 'snapshot', + }); + + log.warn(`New node prover synced`); + const tips = await node.getL2Tips(); + expect(tips.latest.number).toBeLessThanOrEqual(L2_TARGET_BLOCK_NUM); + + const worldState = await node.getWorldStateSyncStatus(); + expect(worldState.latestBlockNumber).toBeLessThanOrEqual(L2_TARGET_BLOCK_NUM); + + log.warn(`Stopping new prover node`); + await node.stop(); + }); }); diff --git a/yarn-project/node-lib/.eslintrc.cjs b/yarn-project/node-lib/.eslintrc.cjs new file mode 100644 index 000000000000..e659927475c0 --- /dev/null +++ b/yarn-project/node-lib/.eslintrc.cjs @@ -0,0 +1 @@ +module.exports = require('@aztec/foundation/eslint'); diff --git a/yarn-project/node-lib/README.md b/yarn-project/node-lib/README.md new file mode 100644 index 000000000000..7b46b05b4454 --- /dev/null +++ b/yarn-project/node-lib/README.md @@ -0,0 +1,3 @@ +# Node Lib + +Shared code for Aztec Nodes and Prover Nodes. diff --git a/yarn-project/node-lib/package.json b/yarn-project/node-lib/package.json new file mode 100644 index 000000000000..a85e0afa1178 --- /dev/null +++ b/yarn-project/node-lib/package.json @@ -0,0 +1,93 @@ +{ + "name": "@aztec/node-lib", + "version": "0.1.0", + "type": "module", + "exports": { + "./actions": "./dest/actions/index.js", + "./config": "./dest/config/index.js" + }, + "inherits": [ + "../package.common.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", + "bb": "node --no-warnings ./dest/bb/index.js", + "test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules ../node_modules/.bin/jest --passWithNoTests --maxWorkers=${JEST_MAX_WORKERS:-8}" + }, + "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" + ], + "testTimeout": 120000, + "setupFiles": [ + "../../foundation/src/jest/setup.mjs" + ] + }, + "dependencies": { + "@aztec/archiver": "workspace:^", + "@aztec/bb-prover": "workspace:^", + "@aztec/blob-sink": "workspace:^", + "@aztec/constants": "workspace:^", + "@aztec/epoch-cache": "workspace:^", + "@aztec/ethereum": "workspace:^", + "@aztec/foundation": "workspace:^", + "@aztec/kv-store": "workspace:^", + "@aztec/merkle-tree": "workspace:^", + "@aztec/p2p": "workspace:^", + "@aztec/protocol-contracts": "workspace:^", + "@aztec/prover-client": "workspace:^", + "@aztec/sequencer-client": "workspace:^", + "@aztec/simulator": "workspace:^", + "@aztec/stdlib": "workspace:^", + "@aztec/telemetry-client": "workspace:^", + "@aztec/validator-client": "workspace:^", + "@aztec/world-state": "workspace:^", + "tslib": "^2.4.0" + }, + "devDependencies": { + "@jest/globals": "^29.5.0", + "@types/jest": "^29.5.0", + "@types/node": "^18.7.23", + "jest": "^29.5.0", + "jest-mock-extended": "^3.0.3", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" + }, + "files": [ + "dest", + "src", + "!*.test.*" + ], + "types": "./dest/index.d.ts", + "engines": { + "node": ">=18" + } +} diff --git a/yarn-project/node-lib/src/actions/index.ts b/yarn-project/node-lib/src/actions/index.ts new file mode 100644 index 000000000000..e700eb7af919 --- /dev/null +++ b/yarn-project/node-lib/src/actions/index.ts @@ -0,0 +1,2 @@ +export * from './snapshot-sync.js'; +export * from './upload-snapshot.js'; diff --git a/yarn-project/aztec-node/src/actions/snapshot-sync.ts b/yarn-project/node-lib/src/actions/snapshot-sync.ts similarity index 95% rename from yarn-project/aztec-node/src/actions/snapshot-sync.ts rename to yarn-project/node-lib/src/actions/snapshot-sync.ts index 18178adf7749..6f21c9727f3d 100644 --- a/yarn-project/aztec-node/src/actions/snapshot-sync.ts +++ b/yarn-project/node-lib/src/actions/snapshot-sync.ts @@ -6,11 +6,12 @@ import { createArchiverStore, } from '@aztec/archiver'; import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants'; -import { type EthereumClientConfig, type L1ContractAddresses, getPublicClient } from '@aztec/ethereum'; +import { type EthereumClientConfig, getPublicClient } from '@aztec/ethereum'; import type { EthAddress } from '@aztec/foundation/eth-address'; import { tryRmDir } from '@aztec/foundation/fs'; import type { Logger } from '@aztec/foundation/log'; import type { DataStoreConfig } from '@aztec/kv-store/config'; +import type { ChainConfig } from '@aztec/stdlib/config'; import { DatabaseVersionManager } from '@aztec/stdlib/database-version'; import { type ReadOnlyFileStore, createReadOnlyFileStore } from '@aztec/stdlib/file-store'; import { @@ -25,19 +26,16 @@ import { NATIVE_WORLD_STATE_DBS, WORLD_STATE_DB_VERSION, WORLD_STATE_DIR } from import { mkdir, mkdtemp, rename } from 'fs/promises'; import { join } from 'path'; -import type { AztecNodeConfig } from '../aztec-node/config.js'; +import type { SharedNodeConfig } from '../config/index.js'; // Half day worth of L1 blocks const MIN_L1_BLOCKS_TO_TRIGGER_REPLACE = 86400 / 2 / 12; -type SnapshotSyncConfig = Pick< - AztecNodeConfig, - 'syncMode' | 'snapshotsUrl' | 'l1ChainId' | 'version' | 'dataDirectory' -> & +type SnapshotSyncConfig = Pick & + Pick & Pick & - DataStoreConfig & + Required & EthereumClientConfig & { - l1Contracts: Pick; minL1BlocksToTriggerReplace?: number; }; diff --git a/yarn-project/aztec-node/src/actions/upload-snapshot.ts b/yarn-project/node-lib/src/actions/upload-snapshot.ts similarity index 92% rename from yarn-project/aztec-node/src/actions/upload-snapshot.ts rename to yarn-project/node-lib/src/actions/upload-snapshot.ts index 842d7faf3fd1..e5d5c3aec5af 100644 --- a/yarn-project/aztec-node/src/actions/upload-snapshot.ts +++ b/yarn-project/node-lib/src/actions/upload-snapshot.ts @@ -1,6 +1,8 @@ import { ARCHIVER_DB_VERSION, type Archiver } from '@aztec/archiver'; import { tryRmDir } from '@aztec/foundation/fs'; import type { Logger } from '@aztec/foundation/log'; +import type { DataStoreConfig } from '@aztec/kv-store/config'; +import type { ChainConfig } from '@aztec/stdlib/config'; import { createFileStore } from '@aztec/stdlib/file-store'; import type { WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server'; import type { SnapshotDataUrls, UploadSnapshotMetadata } from '@aztec/stdlib/snapshots'; @@ -12,7 +14,7 @@ import { mkdtemp } from 'fs/promises'; import { tmpdir } from 'os'; import { join } from 'path'; -import type { AztecNodeConfig } from '../aztec-node/config.js'; +type UploadSnapshotConfig = Pick & Pick; /** * Pauses the archiver and world state sync, creates backups of the archiver and world state lmdb environments, @@ -22,7 +24,7 @@ export async function uploadSnapshot( location: string, archiver: Archiver, worldState: WorldStateSynchronizer, - config: Pick, + config: UploadSnapshotConfig, log: Logger, ) { const store = await createFileStore(location); @@ -47,7 +49,7 @@ export async function uploadSnapshot( async function buildSnapshotMetadata( archiver: Archiver, - config: Pick, + config: UploadSnapshotConfig, ): Promise { const [rollupAddress, l1BlockNumber, { latest }] = await Promise.all([ archiver.getRollupAddress(), diff --git a/yarn-project/node-lib/src/config/index.ts b/yarn-project/node-lib/src/config/index.ts new file mode 100644 index 000000000000..d0b98d1835ae --- /dev/null +++ b/yarn-project/node-lib/src/config/index.ts @@ -0,0 +1,35 @@ +import { type ConfigMappingsType, booleanConfigHelper } from '@aztec/foundation/config'; + +export type SharedNodeConfig = { + /** Whether to populate the genesis state with initial fee juice for the test accounts */ + testAccounts: boolean; + /** Whether to populate the genesis state with initial fee juice for the sponsored FPC */ + sponsoredFPC: boolean; + /** Sync mode: full to always sync via L1, snapshot to download a snapshot if there is no local data, force-snapshot to download even if there is local data. */ + syncMode: 'full' | 'snapshot' | 'force-snapshot'; + /** Base URL for snapshots index. Index file will be searched at `SNAPSHOTS_BASE_URL/aztec-L1_CHAIN_ID-VERSION-ROLLUP_ADDRESS/index.json` */ + snapshotsUrl?: string; +}; + +export const sharedNodeConfigMappings: ConfigMappingsType = { + testAccounts: { + env: 'TEST_ACCOUNTS', + description: 'Whether to populate the genesis state with initial fee juice for the test accounts.', + ...booleanConfigHelper(), + }, + sponsoredFPC: { + env: 'SPONSORED_FPC', + description: 'Whether to populate the genesis state with initial fee juice for the sponsored FPC.', + ...booleanConfigHelper(false), + }, + syncMode: { + env: 'SYNC_MODE', + description: + 'Set sync mode to `full` to always sync via L1, `snapshot` to download a snapshot if there is no local data, `force-snapshot` to download even if there is local data.', + defaultValue: 'snapshot', + }, + snapshotsUrl: { + env: 'SYNC_SNAPSHOTS_URL', + description: 'Base URL for snapshots index.', + }, +}; diff --git a/yarn-project/node-lib/tsconfig.json b/yarn-project/node-lib/tsconfig.json new file mode 100644 index 000000000000..ad71a8c78ad2 --- /dev/null +++ b/yarn-project/node-lib/tsconfig.json @@ -0,0 +1,65 @@ +{ + "extends": "..", + "compilerOptions": { + "outDir": "dest", + "rootDir": "src", + "tsBuildInfoFile": ".tsbuildinfo" + }, + "references": [ + { + "path": "../archiver" + }, + { + "path": "../bb-prover" + }, + { + "path": "../blob-sink" + }, + { + "path": "../constants" + }, + { + "path": "../epoch-cache" + }, + { + "path": "../ethereum" + }, + { + "path": "../foundation" + }, + { + "path": "../kv-store" + }, + { + "path": "../merkle-tree" + }, + { + "path": "../p2p" + }, + { + "path": "../protocol-contracts" + }, + { + "path": "../prover-client" + }, + { + "path": "../sequencer-client" + }, + { + "path": "../simulator" + }, + { + "path": "../stdlib" + }, + { + "path": "../telemetry-client" + }, + { + "path": "../validator-client" + }, + { + "path": "../world-state" + } + ], + "include": ["src"] +} diff --git a/yarn-project/package.json b/yarn-project/package.json index b6d5a8109f43..ef742ceb1dd1 100644 --- a/yarn-project/package.json +++ b/yarn-project/package.json @@ -47,6 +47,7 @@ "l1-artifacts", "merkle-tree", "native", + "node-lib", "ivc-integration", "noir-bb-bench", "noir-contracts.js", diff --git a/yarn-project/prover-node/package.json b/yarn-project/prover-node/package.json index 094cb512f9d0..128fec9887f3 100644 --- a/yarn-project/prover-node/package.json +++ b/yarn-project/prover-node/package.json @@ -62,6 +62,7 @@ "@aztec/foundation": "workspace:^", "@aztec/kv-store": "workspace:^", "@aztec/l1-artifacts": "workspace:^", + "@aztec/node-lib": "workspace:^", "@aztec/noir-protocol-circuits-types": "workspace:^", "@aztec/p2p": "workspace:^", "@aztec/protocol-contracts": "workspace:^", diff --git a/yarn-project/prover-node/src/config.ts b/yarn-project/prover-node/src/config.ts index 924f603d81bb..7afbd08ecbc9 100644 --- a/yarn-project/prover-node/src/config.ts +++ b/yarn-project/prover-node/src/config.ts @@ -1,12 +1,8 @@ import { type ArchiverConfig, archiverConfigMappings } from '@aztec/archiver/config'; import type { ACVMConfig, BBConfig } from '@aztec/bb-prover/config'; -import { - type ConfigMappingsType, - booleanConfigHelper, - getConfigFromMappings, - numberConfigHelper, -} from '@aztec/foundation/config'; +import { type ConfigMappingsType, getConfigFromMappings, numberConfigHelper } from '@aztec/foundation/config'; import { type DataStoreConfig, dataConfigMappings } from '@aztec/kv-store/config'; +import { type SharedNodeConfig, sharedNodeConfigMappings } from '@aztec/node-lib/config'; import { type P2PConfig, p2pConfigMappings } from '@aztec/p2p/config'; import { type ProverAgentConfig, @@ -33,12 +29,8 @@ export type ProverNodeConfig = ArchiverConfig & TxSenderConfig & DataStoreConfig & ProverCoordinationConfig & - SpecificProverNodeConfig & { - /** Whether to populate the genesis state with initial fee juice for the test accounts */ - testAccounts: boolean; - /** Whether to populate the genesis state with initial fee juice for the sponsored FPC */ - sponsoredFPC: boolean; - }; + SharedNodeConfig & + SpecificProverNodeConfig; type SpecificProverNodeConfig = { proverNodeMaxPendingJobs: number; @@ -92,16 +84,7 @@ export const proverNodeConfigMappings: ConfigMappingsType = { ...getTxSenderConfigMappings('PROVER'), ...proverCoordinationConfigMappings, ...specificProverNodeConfigMappings, - testAccounts: { - env: 'TEST_ACCOUNTS', - description: 'Whether to populate the genesis state with initial fee juice for the test accounts.', - ...booleanConfigHelper(false), - }, - sponsoredFPC: { - env: 'SPONSORED_FPC', - description: 'Whether to populate the genesis state with initial fee juice for the sponsored FPC.', - ...booleanConfigHelper(false), - }, + ...sharedNodeConfigMappings, }; export function getProverNodeConfigFromEnv(): ProverNodeConfig { diff --git a/yarn-project/prover-node/src/factory.ts b/yarn-project/prover-node/src/factory.ts index 992dc2091a13..694eeea4eef0 100644 --- a/yarn-project/prover-node/src/factory.ts +++ b/yarn-project/prover-node/src/factory.ts @@ -4,6 +4,7 @@ import { EpochCache } from '@aztec/epoch-cache'; import { L1TxUtils, RollupContract, createEthereumChain, createL1Clients } from '@aztec/ethereum'; import { type Logger, createLogger } from '@aztec/foundation/log'; import type { DataStoreConfig } from '@aztec/kv-store/config'; +import { trySnapshotSync } from '@aztec/node-lib/actions'; import { createProverClient } from '@aztec/prover-client'; import { createAndStartProvingBroker } from '@aztec/prover-client/broker'; import type { ProverCoordination, ProvingJobBroker } from '@aztec/stdlib/interfaces/server'; @@ -37,6 +38,9 @@ export async function createProverNode( const telemetry = deps.telemetry ?? getTelemetryClient(); const blobSinkClient = deps.blobSinkClient ?? createBlobSinkClient(config); const log = deps.log ?? createLogger('prover-node'); + + await trySnapshotSync(config, log); + const archiver = deps.archiver ?? (await createArchiver(config, blobSinkClient, { blockUntilSync: true }, telemetry)); log.verbose(`Created archiver and synced to block ${await archiver.getBlockNumber()}`); diff --git a/yarn-project/prover-node/src/prover-node.ts b/yarn-project/prover-node/src/prover-node.ts index f3af0f403304..2b7cd6d52c1e 100644 --- a/yarn-project/prover-node/src/prover-node.ts +++ b/yarn-project/prover-node/src/prover-node.ts @@ -15,6 +15,7 @@ import { type ProverCoordination, type ProverNodeApi, type Service, + type WorldStateSyncStatus, type WorldStateSynchronizer, tryStop, } from '@aztec/stdlib/interfaces/server'; @@ -163,6 +164,16 @@ export class ProverNode implements EpochMonitorHandler, ProverNodeApi, Traceable this.log.info('Stopped ProverNode'); } + /** Returns world state status. */ + public getWorldStateSyncStatus(): Promise { + return this.worldState.status().then(s => s.syncSummary); + } + + /** Returns archiver status. */ + public getL2Tips() { + return this.l2BlockSource.getL2Tips(); + } + /** * Starts a proving process and returns immediately. */ diff --git a/yarn-project/prover-node/tsconfig.json b/yarn-project/prover-node/tsconfig.json index cdb7c5fdde20..1e82cfa793c0 100644 --- a/yarn-project/prover-node/tsconfig.json +++ b/yarn-project/prover-node/tsconfig.json @@ -33,6 +33,9 @@ { "path": "../l1-artifacts" }, + { + "path": "../node-lib" + }, { "path": "../noir-protocol-circuits-types" }, diff --git a/yarn-project/stdlib/src/interfaces/aztec-node.test.ts b/yarn-project/stdlib/src/interfaces/aztec-node.test.ts index d34c0910cd19..656dd2a2f210 100644 --- a/yarn-project/stdlib/src/interfaces/aztec-node.test.ts +++ b/yarn-project/stdlib/src/interfaces/aztec-node.test.ts @@ -365,6 +365,7 @@ class MockAztecNode implements AztecNode { finalized: { number: 1, hash: `0x01` }, }); } + findLeavesIndexes( blockNumber: number | 'latest', treeId: MerkleTreeId, diff --git a/yarn-project/stdlib/src/interfaces/prover-node.test.ts b/yarn-project/stdlib/src/interfaces/prover-node.test.ts index 0158d7bca8b2..fd08670d9e10 100644 --- a/yarn-project/stdlib/src/interfaces/prover-node.test.ts +++ b/yarn-project/stdlib/src/interfaces/prover-node.test.ts @@ -1,6 +1,8 @@ import { type JsonRpcTestContext, createJsonRpcTestSetup } from '@aztec/foundation/json-rpc/test'; +import type { L2Tips } from '../block/l2_block_source.js'; import { type EpochProvingJobState, type ProverNodeApi, ProverNodeApiSchema } from './prover-node.js'; +import type { WorldStateSyncStatus } from './world_state.js'; describe('ProvingNodeApiSchema', () => { let handler: MockProverNode; @@ -32,9 +34,41 @@ describe('ProvingNodeApiSchema', () => { it('startProof', async () => { await context.client.startProof(1); }); + + it('getL2Tips', async () => { + const result = await context.client.getL2Tips(); + expect(result).toEqual({ + latest: { number: 1, hash: `0x01` }, + proven: { number: 1, hash: `0x01` }, + finalized: { number: 1, hash: `0x01` }, + }); + }); + + it('getWorldStateSyncStatus', async () => { + const response = await context.client.getWorldStateSyncStatus(); + expect(response).toEqual(await handler.getWorldStateSyncStatus()); + }); }); class MockProverNode implements ProverNodeApi { + getWorldStateSyncStatus(): Promise { + return Promise.resolve({ + finalisedBlockNumber: 1, + latestBlockHash: '0x', + latestBlockNumber: 1, + oldestHistoricBlockNumber: 1, + treesAreSynched: true, + }); + } + + getL2Tips(): Promise { + return Promise.resolve({ + latest: { number: 1, hash: `0x01` }, + proven: { number: 1, hash: `0x01` }, + finalized: { number: 1, hash: `0x01` }, + }); + } + getJobs(): Promise<{ uuid: string; status: EpochProvingJobState; epochNumber: number }[]> { return Promise.resolve([ { uuid: 'uuid1', status: 'initialized', epochNumber: 10 }, @@ -45,6 +79,7 @@ class MockProverNode implements ProverNodeApi { { uuid: 'uuid6', status: 'failed', epochNumber: 10 }, ]); } + startProof(epochNumber: number): Promise { expect(typeof epochNumber).toBe('number'); return Promise.resolve(); diff --git a/yarn-project/stdlib/src/interfaces/prover-node.ts b/yarn-project/stdlib/src/interfaces/prover-node.ts index 25f3d2560ab2..b28438708136 100644 --- a/yarn-project/stdlib/src/interfaces/prover-node.ts +++ b/yarn-project/stdlib/src/interfaces/prover-node.ts @@ -1,6 +1,8 @@ import { z } from 'zod'; +import { type L2Tips, L2TipsSchema } from '../block/l2_block_source.js'; import { type ApiSchemaFor, schemas } from '../schemas/index.js'; +import { type WorldStateSyncStatus, WorldStateSyncStatusSchema } from './world_state.js'; const EpochProvingJobState = [ 'initialized', @@ -31,6 +33,10 @@ export interface ProverNodeApi { getJobs(): Promise<{ uuid: string; status: EpochProvingJobState; epochNumber: number }[]>; startProof(epochNumber: number): Promise; + + getL2Tips(): Promise; + + getWorldStateSyncStatus(): Promise; } /** Schemas for prover node API functions. */ @@ -41,4 +47,8 @@ export const ProverNodeApiSchema: ApiSchemaFor = { .returns(z.array(z.object({ uuid: z.string(), status: z.enum(EpochProvingJobState), epochNumber: z.number() }))), startProof: z.function().args(schemas.Integer).returns(z.void()), + + getL2Tips: z.function().args().returns(L2TipsSchema), + + getWorldStateSyncStatus: z.function().args().returns(WorldStateSyncStatusSchema), }; diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index 5208c2d3c9d4..117ad4c26198 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -156,6 +156,7 @@ __metadata: "@aztec/foundation": "workspace:^" "@aztec/kv-store": "workspace:^" "@aztec/merkle-tree": "workspace:^" + "@aztec/node-lib": "workspace:^" "@aztec/p2p": "workspace:^" "@aztec/protocol-contracts": "workspace:^" "@aztec/prover-client": "workspace:^" @@ -928,6 +929,39 @@ __metadata: languageName: unknown linkType: soft +"@aztec/node-lib@workspace:^, @aztec/node-lib@workspace:node-lib": + version: 0.0.0-use.local + resolution: "@aztec/node-lib@workspace:node-lib" + dependencies: + "@aztec/archiver": "workspace:^" + "@aztec/bb-prover": "workspace:^" + "@aztec/blob-sink": "workspace:^" + "@aztec/constants": "workspace:^" + "@aztec/epoch-cache": "workspace:^" + "@aztec/ethereum": "workspace:^" + "@aztec/foundation": "workspace:^" + "@aztec/kv-store": "workspace:^" + "@aztec/merkle-tree": "workspace:^" + "@aztec/p2p": "workspace:^" + "@aztec/protocol-contracts": "workspace:^" + "@aztec/prover-client": "workspace:^" + "@aztec/sequencer-client": "workspace:^" + "@aztec/simulator": "workspace:^" + "@aztec/stdlib": "workspace:^" + "@aztec/telemetry-client": "workspace:^" + "@aztec/validator-client": "workspace:^" + "@aztec/world-state": "workspace:^" + "@jest/globals": "npm:^29.5.0" + "@types/jest": "npm:^29.5.0" + "@types/node": "npm:^18.7.23" + jest: "npm:^29.5.0" + jest-mock-extended: "npm:^3.0.3" + ts-node: "npm:^10.9.1" + tslib: "npm:^2.4.0" + typescript: "npm:^5.0.4" + languageName: unknown + linkType: soft + "@aztec/noir-acvm_js@portal:../noir/packages/acvm_js::locator=%40aztec%2Faztec3-packages%40workspace%3A.": version: 0.0.0-use.local resolution: "@aztec/noir-acvm_js@portal:../noir/packages/acvm_js::locator=%40aztec%2Faztec3-packages%40workspace%3A." @@ -1165,6 +1199,7 @@ __metadata: "@aztec/foundation": "workspace:^" "@aztec/kv-store": "workspace:^" "@aztec/l1-artifacts": "workspace:^" + "@aztec/node-lib": "workspace:^" "@aztec/noir-protocol-circuits-types": "workspace:^" "@aztec/p2p": "workspace:^" "@aztec/protocol-contracts": "workspace:^"