diff --git a/yarn-project/archiver/src/factory.ts b/yarn-project/archiver/src/factory.ts index f7f2d46b44db..c0273939a28b 100644 --- a/yarn-project/archiver/src/factory.ts +++ b/yarn-project/archiver/src/factory.ts @@ -7,7 +7,6 @@ import { Buffer32 } from '@aztec/foundation/buffer'; import { merge } from '@aztec/foundation/collection'; import { Fr } from '@aztec/foundation/curves/bn254'; import { DateProvider } from '@aztec/foundation/timer'; -import type { DataStoreConfig } from '@aztec/kv-store/config'; import { createStore } from '@aztec/kv-store/lmdb-v2'; import { protocolContractNames } from '@aztec/protocol-contracts'; import { BundledProtocolContractsProvider } from '@aztec/protocol-contracts/providers/bundle'; @@ -15,6 +14,7 @@ import { FunctionType, decodeFunctionSignature } from '@aztec/stdlib/abi'; import type { ArchiverEmitter } from '@aztec/stdlib/block'; import { type ContractClassPublic, computePublicBytecodeCommitment } from '@aztec/stdlib/contract'; import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers'; +import type { DataStoreConfig } from '@aztec/stdlib/kv-store'; import { getTelemetryClient } from '@aztec/telemetry-client'; import { EventEmitter } from 'events'; diff --git a/yarn-project/aztec-node/src/aztec-node/config.ts b/yarn-project/aztec-node/src/aztec-node/config.ts index 27d433cc6db4..5c74334a94be 100644 --- a/yarn-project/aztec-node/src/aztec-node/config.ts +++ b/yarn-project/aztec-node/src/aztec-node/config.ts @@ -3,7 +3,6 @@ import { type GenesisStateConfig, genesisStateConfigMappings } from '@aztec/ethe import { type L1ContractAddresses, l1ContractAddressesMapping } from '@aztec/ethereum/l1-contract-addresses'; import { type ConfigMappingsType, booleanConfigHelper, getConfigFromMappings } from '@aztec/foundation/config'; import { EthAddress } from '@aztec/foundation/eth-address'; -import { type DataStoreConfig, dataConfigMappings } from '@aztec/kv-store/config'; import { type KeyStore, type ValidatorKeyStore, @@ -27,6 +26,7 @@ import { slasherConfigMappings } from '@aztec/slasher'; import { AztecAddress } from '@aztec/stdlib/aztec-address'; import { type NodeRPCConfig, nodeRpcConfigMappings } from '@aztec/stdlib/config'; import type { SlasherConfig } from '@aztec/stdlib/interfaces/server'; +import { type DataStoreConfig, dataConfigMappings } from '@aztec/stdlib/kv-store'; import { type ValidatorClientConfig, validatorClientConfigMappings } from '@aztec/validator-client/config'; import { type WorldStateConfig, worldStateConfigMappings } from '@aztec/world-state/config'; diff --git a/yarn-project/aztec-node/src/sentinel/factory.ts b/yarn-project/aztec-node/src/sentinel/factory.ts index 15f0caf22582..251b9086a627 100644 --- a/yarn-project/aztec-node/src/sentinel/factory.ts +++ b/yarn-project/aztec-node/src/sentinel/factory.ts @@ -1,10 +1,10 @@ import type { EpochCache } from '@aztec/epoch-cache'; import { createLogger } from '@aztec/foundation/log'; -import type { DataStoreConfig } from '@aztec/kv-store/config'; import { createStore } from '@aztec/kv-store/lmdb-v2'; import type { P2PClient } from '@aztec/p2p'; import type { L2BlockSource } from '@aztec/stdlib/block'; import type { SlasherConfig } from '@aztec/stdlib/interfaces/server'; +import type { DataStoreConfig } from '@aztec/stdlib/kv-store'; import type { SentinelConfig } from './config.js'; import { Sentinel } from './sentinel.js'; diff --git a/yarn-project/aztec/src/cli/aztec_start_options.ts b/yarn-project/aztec/src/cli/aztec_start_options.ts index 863291bf19c1..269609bd1911 100644 --- a/yarn-project/aztec/src/cli/aztec_start_options.ts +++ b/yarn-project/aztec/src/cli/aztec_start_options.ts @@ -12,7 +12,6 @@ import { isBooleanConfigValue, omitConfigMappings, } from '@aztec/foundation/config'; -import { dataConfigMappings } from '@aztec/kv-store/config'; import { sharedNodeConfigMappings } from '@aztec/node-lib/config'; import { bootnodeConfigMappings, p2pConfigMappings } from '@aztec/p2p/config'; import { proverAgentConfigMappings, proverBrokerConfigMappings } from '@aztec/prover-client/broker/config'; @@ -20,6 +19,7 @@ import { proverNodeConfigMappings } from '@aztec/prover-node/config'; import { allPxeConfigMappings } from '@aztec/pxe/config'; import { sequencerClientConfigMappings } from '@aztec/sequencer-client/config'; import { chainConfigMappings, nodeRpcConfigMappings } from '@aztec/stdlib/config'; +import { dataConfigMappings } from '@aztec/stdlib/kv-store'; import { telemetryClientConfigMappings } from '@aztec/telemetry-client/config'; import { worldStateConfigMappings } from '@aztec/world-state/config'; diff --git a/yarn-project/aztec/src/cli/cmds/start_archiver.ts b/yarn-project/aztec/src/cli/cmds/start_archiver.ts index d4c95bcd7e6e..2063fadbb6ed 100644 --- a/yarn-project/aztec/src/cli/cmds/start_archiver.ts +++ b/yarn-project/aztec/src/cli/cmds/start_archiver.ts @@ -3,8 +3,8 @@ import { createLogger } from '@aztec/aztec.js/log'; import { type BlobClientConfig, blobClientConfigMapping, createBlobClient } from '@aztec/blob-client/client'; import { getL1Config } from '@aztec/cli/config'; import type { NamespacedApiHandlers } from '@aztec/foundation/json-rpc/server'; -import { type DataStoreConfig, dataConfigMappings } from '@aztec/kv-store/config'; import { ArchiverApiSchema } from '@aztec/stdlib/interfaces/server'; +import { type DataStoreConfig, dataConfigMappings } from '@aztec/stdlib/kv-store'; import { getConfigEnvVars as getTelemetryClientConfig, initTelemetryClient } from '@aztec/telemetry-client'; import { extractRelevantOptions } from '../util.js'; diff --git a/yarn-project/bot/src/config.ts b/yarn-project/bot/src/config.ts index 1e1b104be9b9..3efc3c977348 100644 --- a/yarn-project/bot/src/config.ts +++ b/yarn-project/bot/src/config.ts @@ -11,9 +11,9 @@ import { secretStringConfigHelper, } from '@aztec/foundation/config'; import { Fr } from '@aztec/foundation/curves/bn254'; -import { type DataStoreConfig, dataConfigMappings } from '@aztec/kv-store/config'; import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree'; import { protocolContractsHash } from '@aztec/protocol-contracts'; +import { type DataStoreConfig, dataConfigMappings } from '@aztec/stdlib/kv-store'; import { schemas, zodFor } from '@aztec/stdlib/schemas'; import type { ComponentsVersions } from '@aztec/stdlib/versioning'; @@ -130,7 +130,6 @@ export const BotConfigSchema = zodFor()( l1Mnemonic: undefined, l1PrivateKey: undefined, senderPrivateKey: undefined, - dataDirectory: undefined, dataStoreMapSizeKb: 1_024 * 1_024, ...config, })), diff --git a/yarn-project/foundation/src/config/env_var.ts b/yarn-project/foundation/src/config/env_var.ts index 942316271146..666bd5a54f19 100644 --- a/yarn-project/foundation/src/config/env_var.ts +++ b/yarn-project/foundation/src/config/env_var.ts @@ -62,6 +62,7 @@ export type EnvVar = | 'BLOB_SINK_MAP_SIZE_KB' | 'P2P_STORE_MAP_SIZE_KB' | 'PROVER_BROKER_STORE_MAP_SIZE_KB' + | 'SIGNING_PROTECTION_MAP_SIZE_KB' | 'WS_DB_MAP_SIZE_KB' | 'ARCHIVE_TREE_MAP_SIZE_KB' | 'NULLIFIER_TREE_MAP_SIZE_KB' diff --git a/yarn-project/kv-store/package.json b/yarn-project/kv-store/package.json index e2edcd04f409..a6738123cb71 100644 --- a/yarn-project/kv-store/package.json +++ b/yarn-project/kv-store/package.json @@ -8,8 +8,7 @@ "./lmdb": "./dest/lmdb/index.js", "./lmdb-v2": "./dest/lmdb-v2/index.js", "./indexeddb": "./dest/indexeddb/index.js", - "./stores": "./dest/stores/index.js", - "./config": "./dest/config.js" + "./stores": "./dest/stores/index.js" }, "scripts": { "build": "yarn clean && ../scripts/tsc.sh", diff --git a/yarn-project/kv-store/src/indexeddb/index.ts b/yarn-project/kv-store/src/indexeddb/index.ts index e913c7d33fcc..e0dd61a961e8 100644 --- a/yarn-project/kv-store/src/indexeddb/index.ts +++ b/yarn-project/kv-store/src/indexeddb/index.ts @@ -1,6 +1,6 @@ import { type Logger, createLogger } from '@aztec/foundation/log'; +import type { DataStoreConfig } from '@aztec/stdlib/kv-store'; -import type { DataStoreConfig } from '../config.js'; import { initStoreForRollupAndSchemaVersion } from '../utils.js'; import { AztecIndexedDBStore } from './store.js'; diff --git a/yarn-project/kv-store/src/lmdb-v2/factory.ts b/yarn-project/kv-store/src/lmdb-v2/factory.ts index c61a352b070c..73a8c48295ca 100644 --- a/yarn-project/kv-store/src/lmdb-v2/factory.ts +++ b/yarn-project/kv-store/src/lmdb-v2/factory.ts @@ -1,12 +1,12 @@ import { EthAddress } from '@aztec/foundation/eth-address'; import { type LoggerBindings, createLogger } from '@aztec/foundation/log'; import { DatabaseVersionManager } from '@aztec/stdlib/database-version/manager'; +import type { DataStoreConfig } from '@aztec/stdlib/kv-store'; import { mkdir, mkdtemp, rm } from 'fs/promises'; import { tmpdir } from 'os'; import { join } from 'path'; -import type { DataStoreConfig } from '../config.js'; import { AztecLMDBStoreV2 } from './store.js'; const MAX_READERS = 16; diff --git a/yarn-project/kv-store/src/lmdb/index.ts b/yarn-project/kv-store/src/lmdb/index.ts index bcaf1f307d8c..aac0204c4655 100644 --- a/yarn-project/kv-store/src/lmdb/index.ts +++ b/yarn-project/kv-store/src/lmdb/index.ts @@ -1,8 +1,8 @@ import { type Logger, createLogger } from '@aztec/foundation/log'; +import type { DataStoreConfig } from '@aztec/stdlib/kv-store'; import { join } from 'path'; -import type { DataStoreConfig } from '../config.js'; import { initStoreForRollupAndSchemaVersion } from '../utils.js'; import { AztecLmdbStore } from './store.js'; diff --git a/yarn-project/node-lib/src/actions/snapshot-sync.ts b/yarn-project/node-lib/src/actions/snapshot-sync.ts index d60760bf6aa1..03d90ca67c51 100644 --- a/yarn-project/node-lib/src/actions/snapshot-sync.ts +++ b/yarn-project/node-lib/src/actions/snapshot-sync.ts @@ -5,11 +5,11 @@ import type { L1ContractsConfig } from '@aztec/ethereum/config'; 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 { P2P_STORE_NAME } from '@aztec/p2p'; import type { ChainConfig } from '@aztec/stdlib/config'; import { DatabaseVersionManager } from '@aztec/stdlib/database-version/manager'; import { type ReadOnlyFileStore, createReadOnlyFileStore } from '@aztec/stdlib/file-store'; +import type { DataStoreConfig } from '@aztec/stdlib/kv-store'; import { type SnapshotMetadata, type SnapshotsIndexMetadata, @@ -31,7 +31,8 @@ type SnapshotSyncConfig = Pick & Pick & Pick & Pick & - Required & + DataStoreConfig & + Required> & EthereumClientConfig & { snapshotsUrls?: string[]; minL1BlocksToTriggerReplace?: number; diff --git a/yarn-project/node-lib/src/actions/upload-snapshot.ts b/yarn-project/node-lib/src/actions/upload-snapshot.ts index 82b62a350dce..0bb1446b4314 100644 --- a/yarn-project/node-lib/src/actions/upload-snapshot.ts +++ b/yarn-project/node-lib/src/actions/upload-snapshot.ts @@ -1,10 +1,10 @@ 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 { DataStoreConfig } from '@aztec/stdlib/kv-store'; import { uploadSnapshotToIndex } from '@aztec/stdlib/snapshots'; import { WORLD_STATE_DB_VERSION } from '@aztec/world-state'; diff --git a/yarn-project/node-lib/src/factories/l1_tx_utils.ts b/yarn-project/node-lib/src/factories/l1_tx_utils.ts index d3a441c66bb9..4799c3f93e08 100644 --- a/yarn-project/node-lib/src/factories/l1_tx_utils.ts +++ b/yarn-project/node-lib/src/factories/l1_tx_utils.ts @@ -8,8 +8,8 @@ import { omit } from '@aztec/foundation/collection'; import type { EthAddress } from '@aztec/foundation/eth-address'; import { createLogger } from '@aztec/foundation/log'; import type { DateProvider } from '@aztec/foundation/timer'; -import type { DataStoreConfig } from '@aztec/kv-store/config'; import { createStore } from '@aztec/kv-store/lmdb-v2'; +import type { DataStoreConfig } from '@aztec/stdlib/kv-store'; import type { TelemetryClient } from '@aztec/telemetry-client'; import type { L1TxScope } from '../metrics/l1_tx_metrics.js'; diff --git a/yarn-project/p2p/src/client/factory.ts b/yarn-project/p2p/src/client/factory.ts index 0895c1b624cc..9042d48ec9ae 100644 --- a/yarn-project/p2p/src/client/factory.ts +++ b/yarn-project/p2p/src/client/factory.ts @@ -3,12 +3,12 @@ import { BlockNumber } from '@aztec/foundation/branded-types'; import { type Logger, createLogger } from '@aztec/foundation/log'; import { DateProvider } from '@aztec/foundation/timer'; import type { AztecAsyncKVStore } from '@aztec/kv-store'; -import type { DataStoreConfig } from '@aztec/kv-store/config'; import { AztecLMDBStoreV2, createStore } from '@aztec/kv-store/lmdb-v2'; import type { L2BlockSource } from '@aztec/stdlib/block'; import type { ChainConfig } from '@aztec/stdlib/config'; import type { ContractDataSource } from '@aztec/stdlib/contract'; import type { AztecNode, ClientProtocolCircuitVerifier, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server'; +import type { DataStoreConfig } from '@aztec/stdlib/kv-store'; import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client'; import { P2PClient } from '../client/p2p_client.js'; diff --git a/yarn-project/p2p/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts b/yarn-project/p2p/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts index e1f054b98a02..a76672a1e1de 100644 --- a/yarn-project/p2p/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +++ b/yarn-project/p2p/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts @@ -3,11 +3,11 @@ import { SecretValue } from '@aztec/foundation/config'; import { createLogger } from '@aztec/foundation/log'; import { sleep } from '@aztec/foundation/sleep'; import { DateProvider, Timer, executeTimeout } from '@aztec/foundation/timer'; -import type { DataStoreConfig } from '@aztec/kv-store/config'; import { openTmpStore } from '@aztec/kv-store/lmdb-v2'; import type { L2BlockSource } from '@aztec/stdlib/block'; import type { ContractDataSource } from '@aztec/stdlib/contract'; import type { ClientProtocolCircuitVerifier } from '@aztec/stdlib/interfaces/server'; +import type { DataStoreConfig } from '@aztec/stdlib/kv-store'; import { PeerErrorSeverity } from '@aztec/stdlib/p2p'; import type { Tx, TxValidationResult } from '@aztec/stdlib/tx'; import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client'; diff --git a/yarn-project/p2p/src/config.ts b/yarn-project/p2p/src/config.ts index fc27123df79c..f3dbefe44d4d 100644 --- a/yarn-project/p2p/src/config.ts +++ b/yarn-project/p2p/src/config.ts @@ -11,7 +11,6 @@ import { secretStringConfigHelper, } from '@aztec/foundation/config'; import { Fr } from '@aztec/foundation/curves/bn254'; -import { type DataStoreConfig, dataConfigMappings } from '@aztec/kv-store/config'; import { FunctionSelector } from '@aztec/stdlib/abi/function-selector'; import { AztecAddress } from '@aztec/stdlib/aztec-address'; import { @@ -21,6 +20,7 @@ import { chainConfigMappings, sharedSequencerConfigMappings, } from '@aztec/stdlib/config'; +import { type DataStoreConfig, dataConfigMappings } from '@aztec/stdlib/kv-store'; import { type BatchTxRequesterConfig, diff --git a/yarn-project/p2p/src/test-helpers/make-test-p2p-clients.ts b/yarn-project/p2p/src/test-helpers/make-test-p2p-clients.ts index 1bb554f79b06..3ba6375f5d3c 100644 --- a/yarn-project/p2p/src/test-helpers/make-test-p2p-clients.ts +++ b/yarn-project/p2p/src/test-helpers/make-test-p2p-clients.ts @@ -4,9 +4,9 @@ import { SecretValue } from '@aztec/foundation/config'; import { type Logger, createLogger } from '@aztec/foundation/log'; import { retryUntil } from '@aztec/foundation/retry'; import { sleep } from '@aztec/foundation/sleep'; -import type { DataStoreConfig } from '@aztec/kv-store/config'; import { openTmpStore } from '@aztec/kv-store/lmdb-v2'; import type { WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server'; +import type { DataStoreConfig } from '@aztec/stdlib/kv-store'; import { createP2PClient } from '../client/index.js'; import type { P2PClient } from '../client/p2p_client.js'; diff --git a/yarn-project/p2p/src/test-helpers/reqresp-nodes.ts b/yarn-project/p2p/src/test-helpers/reqresp-nodes.ts index f0e6f04232e2..72f9145ab1fb 100644 --- a/yarn-project/p2p/src/test-helpers/reqresp-nodes.ts +++ b/yarn-project/p2p/src/test-helpers/reqresp-nodes.ts @@ -2,7 +2,6 @@ import type { EpochCache } from '@aztec/epoch-cache'; import { timesParallel } from '@aztec/foundation/collection'; import { SecretValue } from '@aztec/foundation/config'; import { createLogger } from '@aztec/foundation/log'; -import type { DataStoreConfig } from '@aztec/kv-store/config'; import { openTmpStore } from '@aztec/kv-store/lmdb-v2'; import type { L2BlockSource } from '@aztec/stdlib/block'; import { type ChainConfig, emptyChainConfig } from '@aztec/stdlib/config'; @@ -12,6 +11,7 @@ import type { IVCProofVerificationResult, WorldStateSynchronizer, } from '@aztec/stdlib/interfaces/server'; +import type { DataStoreConfig } from '@aztec/stdlib/kv-store'; import type { Tx } from '@aztec/stdlib/tx'; import { compressComponentVersions } from '@aztec/stdlib/versioning'; import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client'; diff --git a/yarn-project/p2p/src/testbench/p2p_client_testbench_worker.ts b/yarn-project/p2p/src/testbench/p2p_client_testbench_worker.ts index dc2fa88beb89..e8cb8ac7ca46 100644 --- a/yarn-project/p2p/src/testbench/p2p_client_testbench_worker.ts +++ b/yarn-project/p2p/src/testbench/p2p_client_testbench_worker.ts @@ -12,13 +12,13 @@ import { Fr } from '@aztec/foundation/curves/bn254'; import { type Logger, createLogger } from '@aztec/foundation/log'; import { sleep } from '@aztec/foundation/sleep'; import { DateProvider, Timer } from '@aztec/foundation/timer'; -import type { DataStoreConfig } from '@aztec/kv-store/config'; import { openTmpStore } from '@aztec/kv-store/lmdb-v2'; import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree'; import { protocolContractsHash } from '@aztec/protocol-contracts'; import type { L2BlockSource } from '@aztec/stdlib/block'; import type { ContractDataSource } from '@aztec/stdlib/contract'; import type { ClientProtocolCircuitVerifier, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server'; +import type { DataStoreConfig } from '@aztec/stdlib/kv-store'; import { type BlockProposal, P2PMessage } from '@aztec/stdlib/p2p'; import { ChonkProof } from '@aztec/stdlib/proofs'; import { makeAztecAddress, makeBlockHeader, makeBlockProposal, mockTx } from '@aztec/stdlib/testing'; diff --git a/yarn-project/p2p/src/util.test.ts b/yarn-project/p2p/src/util.test.ts index 0ae0a6c958aa..fec51a67f641 100644 --- a/yarn-project/p2p/src/util.test.ts +++ b/yarn-project/p2p/src/util.test.ts @@ -1,8 +1,8 @@ import { SecretValue } from '@aztec/foundation/config'; import { createLogger } from '@aztec/foundation/log'; import type { AztecAsyncKVStore } from '@aztec/kv-store'; -import type { DataStoreConfig } from '@aztec/kv-store/config'; import { openTmpStore } from '@aztec/kv-store/lmdb-v2'; +import type { DataStoreConfig } from '@aztec/stdlib/kv-store'; import { generateKeyPair, marshalPrivateKey } from '@libp2p/crypto/keys'; import { createSecp256k1PeerId } from '@libp2p/peer-id-factory'; diff --git a/yarn-project/p2p/src/util.ts b/yarn-project/p2p/src/util.ts index ab14817d8b47..37bba2f5f0b9 100644 --- a/yarn-project/p2p/src/util.ts +++ b/yarn-project/p2p/src/util.ts @@ -1,7 +1,7 @@ import { SecretValue } from '@aztec/foundation/config'; import type { Logger } from '@aztec/foundation/log'; import type { AztecAsyncKVStore, AztecAsyncSingleton } from '@aztec/kv-store'; -import type { DataStoreConfig } from '@aztec/kv-store/config'; +import type { DataStoreConfig } from '@aztec/stdlib/kv-store'; import type { GossipSub } from '@chainsafe/libp2p-gossipsub'; import { generateKeyPair, marshalPrivateKey, unmarshalPrivateKey } from '@libp2p/crypto/keys'; diff --git a/yarn-project/prover-client/src/proving_broker/config.ts b/yarn-project/prover-client/src/proving_broker/config.ts index d8b875858ad8..6a5672117b98 100644 --- a/yarn-project/prover-client/src/proving_broker/config.ts +++ b/yarn-project/prover-client/src/proving_broker/config.ts @@ -6,8 +6,8 @@ import { numberConfigHelper, } from '@aztec/foundation/config'; import { pickConfigMappings } from '@aztec/foundation/config'; -import { type DataStoreConfig, dataConfigMappings } from '@aztec/kv-store/config'; import { type ChainConfig, chainConfigMappings } from '@aztec/stdlib/config'; +import { type DataStoreConfig, dataConfigMappings } from '@aztec/stdlib/kv-store'; import { ProvingRequestType } from '@aztec/stdlib/proofs'; import { z } from 'zod'; diff --git a/yarn-project/prover-node/src/actions/rerun-epoch-proving-job.ts b/yarn-project/prover-node/src/actions/rerun-epoch-proving-job.ts index 2108d5a09342..2e619fa93be5 100644 --- a/yarn-project/prover-node/src/actions/rerun-epoch-proving-job.ts +++ b/yarn-project/prover-node/src/actions/rerun-epoch-proving-job.ts @@ -1,10 +1,10 @@ import { createArchiverStore } from '@aztec/archiver'; import type { L1ContractsConfig } from '@aztec/ethereum/config'; import type { Logger } from '@aztec/foundation/log'; -import type { DataStoreConfig } from '@aztec/kv-store/config'; import { type ProverClientConfig, createProverClient } from '@aztec/prover-client'; import { ProverBrokerConfig, createAndStartProvingBroker } from '@aztec/prover-client/broker'; import { PublicProcessorFactory } from '@aztec/simulator/server'; +import type { DataStoreConfig } from '@aztec/stdlib/kv-store'; import { getTelemetryClient } from '@aztec/telemetry-client'; import { createWorldState } from '@aztec/world-state'; diff --git a/yarn-project/prover-node/src/actions/upload-epoch-proof-failure.ts b/yarn-project/prover-node/src/actions/upload-epoch-proof-failure.ts index 7717ac9cebfb..3643f5d5869c 100644 --- a/yarn-project/prover-node/src/actions/upload-epoch-proof-failure.ts +++ b/yarn-project/prover-node/src/actions/upload-epoch-proof-failure.ts @@ -3,11 +3,11 @@ import { tryRmDir } from '@aztec/foundation/fs'; import { jsonStringify } from '@aztec/foundation/json-rpc'; import type { Logger } from '@aztec/foundation/log'; import { isoDate } from '@aztec/foundation/string'; -import type { DataStoreConfig } from '@aztec/kv-store/config'; import { buildSnapshotMetadata, createBackups } from '@aztec/node-lib/actions'; import type { ChainConfig } from '@aztec/stdlib/config'; import { type FileStore, createFileStore } from '@aztec/stdlib/file-store'; import type { WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server'; +import type { DataStoreConfig } from '@aztec/stdlib/kv-store'; import { type UploadSnapshotMetadata, getBasePath, uploadSnapshotData } from '@aztec/stdlib/snapshots'; import { WORLD_STATE_DB_VERSION } from '@aztec/world-state'; diff --git a/yarn-project/prover-node/src/config.ts b/yarn-project/prover-node/src/config.ts index 9ba657d77f0a..3a0919845bac 100644 --- a/yarn-project/prover-node/src/config.ts +++ b/yarn-project/prover-node/src/config.ts @@ -6,7 +6,6 @@ import { numberConfigHelper, pickConfigMappings, } from '@aztec/foundation/config'; -import { type DataStoreConfig, dataConfigMappings } from '@aztec/kv-store/config'; import { type KeyStoreConfig, keyStoreConfigMappings } from '@aztec/node-keystore/config'; import { ethPrivateKeySchema } from '@aztec/node-keystore/schemas'; import type { KeyStore } from '@aztec/node-keystore/types'; @@ -24,6 +23,7 @@ import { proverPublisherConfigMappings, proverTxSenderConfigMappings, } from '@aztec/sequencer-client/config'; +import { type DataStoreConfig, dataConfigMappings } from '@aztec/stdlib/kv-store'; export type ProverNodeConfig = ProverClientUserConfig & ProverPublisherConfig & diff --git a/yarn-project/prover-node/src/prover-node.ts b/yarn-project/prover-node/src/prover-node.ts index 8adf039d7651..aeb07e70587f 100644 --- a/yarn-project/prover-node/src/prover-node.ts +++ b/yarn-project/prover-node/src/prover-node.ts @@ -7,7 +7,6 @@ import type { Fr } from '@aztec/foundation/curves/bn254'; import { memoize } from '@aztec/foundation/decorators'; import { createLogger } from '@aztec/foundation/log'; import { DateProvider } from '@aztec/foundation/timer'; -import type { DataStoreConfig } from '@aztec/kv-store/config'; import { PublicProcessorFactory } from '@aztec/simulator/server'; import type { L2BlockSource } from '@aztec/stdlib/block'; import type { Checkpoint } from '@aztec/stdlib/checkpoint'; @@ -24,6 +23,7 @@ import { type WorldStateSynchronizer, tryStop, } from '@aztec/stdlib/interfaces/server'; +import type { DataStoreConfig } from '@aztec/stdlib/kv-store'; import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging'; import type { Tx } from '@aztec/stdlib/tx'; import { diff --git a/yarn-project/pxe/src/config/index.ts b/yarn-project/pxe/src/config/index.ts index 43562d65483c..4f7ac29d3b9a 100644 --- a/yarn-project/pxe/src/config/index.ts +++ b/yarn-project/pxe/src/config/index.ts @@ -5,8 +5,8 @@ import { numberConfigHelper, parseBooleanEnv, } from '@aztec/foundation/config'; -import { type DataStoreConfig, dataConfigMappings } from '@aztec/kv-store/config'; import { type ChainConfig, chainConfigMappings } from '@aztec/stdlib/config'; +import { type DataStoreConfig, dataConfigMappings } from '@aztec/stdlib/kv-store'; export { getPackageInfo } from './package_info.js'; diff --git a/yarn-project/sequencer-client/src/sequencer/checkpoint_voter.ha.integration.test.ts b/yarn-project/sequencer-client/src/sequencer/checkpoint_voter.ha.integration.test.ts index 254485252ec5..a01c498a8969 100644 --- a/yarn-project/sequencer-client/src/sequencer/checkpoint_voter.ha.integration.test.ts +++ b/yarn-project/sequencer-client/src/sequencer/checkpoint_voter.ha.integration.test.ts @@ -257,6 +257,7 @@ describe('CheckpointVoter HA Integration', () => { signingTimeoutMs: 3000, maxStuckDutiesAgeMs: 72000, databaseUrl: 'postgresql://test', + dataStoreMapSizeKb: 128 * 1024 * 1024, }; // Create HA signer with pglite pool diff --git a/yarn-project/slasher/src/factory/create_facade.ts b/yarn-project/slasher/src/factory/create_facade.ts index 6787fc65ce75..0a2010acac01 100644 --- a/yarn-project/slasher/src/factory/create_facade.ts +++ b/yarn-project/slasher/src/factory/create_facade.ts @@ -7,10 +7,10 @@ import { unique } from '@aztec/foundation/collection'; import { EthAddress } from '@aztec/foundation/eth-address'; import { createLogger } from '@aztec/foundation/log'; import { DateProvider } from '@aztec/foundation/timer'; -import type { DataStoreConfig } from '@aztec/kv-store/config'; import { createStore } from '@aztec/kv-store/lmdb-v2'; import { getSlotAtTimestamp } from '@aztec/stdlib/epoch-helpers'; import type { SlasherConfig } from '@aztec/stdlib/interfaces/server'; +import type { DataStoreConfig } from '@aztec/stdlib/kv-store'; import { SlasherClientFacade } from '../slasher_client_facade.js'; import type { SlasherClientInterface } from '../slasher_client_interface.js'; diff --git a/yarn-project/slasher/src/factory/create_implementation.ts b/yarn-project/slasher/src/factory/create_implementation.ts index 0c6eb8ce6d76..cf68df692af2 100644 --- a/yarn-project/slasher/src/factory/create_implementation.ts +++ b/yarn-project/slasher/src/factory/create_implementation.ts @@ -9,9 +9,9 @@ import type { SlotNumber } from '@aztec/foundation/branded-types'; import { EthAddress } from '@aztec/foundation/eth-address'; import { createLogger } from '@aztec/foundation/log'; import { DateProvider } from '@aztec/foundation/timer'; -import type { DataStoreConfig } from '@aztec/kv-store/config'; import { AztecLMDBStoreV2 } from '@aztec/kv-store/lmdb-v2'; import type { SlasherConfig } from '@aztec/stdlib/interfaces/server'; +import type { DataStoreConfig } from '@aztec/stdlib/kv-store'; import { SlashFactoryContract } from '@aztec/stdlib/l1-contracts'; import { EmpireSlasherClient, type EmpireSlasherSettings } from '../empire_slasher_client.js'; diff --git a/yarn-project/slasher/src/slasher_client_facade.ts b/yarn-project/slasher/src/slasher_client_facade.ts index 0ef4a677ac0a..d07cb7d74e40 100644 --- a/yarn-project/slasher/src/slasher_client_facade.ts +++ b/yarn-project/slasher/src/slasher_client_facade.ts @@ -5,9 +5,9 @@ import type { SlotNumber } from '@aztec/foundation/branded-types'; import { EthAddress } from '@aztec/foundation/eth-address'; import { createLogger } from '@aztec/foundation/log'; import { DateProvider } from '@aztec/foundation/timer'; -import type { DataStoreConfig } from '@aztec/kv-store/config'; import { AztecLMDBStoreV2 } from '@aztec/kv-store/lmdb-v2'; import type { SlasherConfig } from '@aztec/stdlib/interfaces/server'; +import type { DataStoreConfig } from '@aztec/stdlib/kv-store'; import type { Offense, ProposerSlashAction, SlashPayloadRound } from '@aztec/stdlib/slashing'; import { createSlasherImplementation } from './factory/create_implementation.js'; diff --git a/yarn-project/stdlib/package.json b/yarn-project/stdlib/package.json index ede741f4ca4e..9fcae1f5e6d4 100644 --- a/yarn-project/stdlib/package.json +++ b/yarn-project/stdlib/package.json @@ -63,7 +63,8 @@ "./slashing": "./dest/slashing/index.js", "./l1-contracts": "./dest/l1-contracts/index.js", "./world-state": "./dest/world-state/index.js", - "./timetable": "./dest/timetable/index.js" + "./timetable": "./dest/timetable/index.js", + "./kv-store": "./dest/kv-store/index.js" }, "typedocOptions": { "entryPoints": [ diff --git a/yarn-project/stdlib/src/ha-signing/config.ts b/yarn-project/stdlib/src/ha-signing/config.ts index a766fac15e99..69107f687be0 100644 --- a/yarn-project/stdlib/src/ha-signing/config.ts +++ b/yarn-project/stdlib/src/ha-signing/config.ts @@ -13,14 +13,9 @@ import type { ZodFor } from '@aztec/foundation/schemas'; import { z } from 'zod'; /** - * Configuration for the Validator HA Signer - * - * This config is used for distributed locking and slashing protection - * when running multiple validator nodes in a high-availability setup. + * Base signing protection configuration shared by both HA (Postgres) and local (LMDB) signers. */ -export interface ValidatorHASignerConfig { - /** Whether HA signing / slashing protection is enabled */ - haSigningEnabled: boolean; +export interface BaseSignerConfig { /** L1 contract addresses (rollup address required) */ l1Contracts: Pick; /** Unique identifier for this node */ @@ -33,30 +28,9 @@ export interface ValidatorHASignerConfig { maxStuckDutiesAgeMs?: number; /** Optional: clean up old duties after this many hours (disabled if not set) */ cleanupOldDutiesAfterHours?: number; - /** - * PostgreSQL connection string - * Format: postgresql://user:password@host:port/database - */ - databaseUrl?: string; - /** - * PostgreSQL connection pool configuration - */ - /** Maximum number of clients in the pool (default: 10) */ - poolMaxCount?: number; - /** Minimum number of clients in the pool (default: 0) */ - poolMinCount?: number; - /** Idle timeout in milliseconds (default: 10000) */ - poolIdleTimeoutMs?: number; - /** Connection timeout in milliseconds (default: 0, no timeout) */ - poolConnectionTimeoutMs?: number; } -export const validatorHASignerConfigMappings: ConfigMappingsType = { - haSigningEnabled: { - env: 'VALIDATOR_HA_SIGNING_ENABLED', - description: 'Whether HA signing / slashing protection is enabled', - ...booleanConfigHelper(false), - }, +export const baseSignerConfigMappings: ConfigMappingsType = { l1Contracts: { description: 'L1 contract addresses (rollup address required)', nested: { @@ -91,6 +65,47 @@ export const validatorHASignerConfigMappings: ConfigMappingsType; + +/** + * Configuration for the Validator HA Signer. + * + * Extends BaseSignerConfig with a flag to enable HA mode and Postgres connection settings. + */ +export interface ValidatorHASignerConfig extends BaseSignerConfig { + /** Whether HA signing / slashing protection is enabled */ + haSigningEnabled: boolean; + /** + * PostgreSQL connection string + * Format: postgresql://user:password@host:port/database + */ + databaseUrl?: string; + /** Maximum number of clients in the pool (default: 10) */ + poolMaxCount?: number; + /** Minimum number of clients in the pool (default: 0) */ + poolMinCount?: number; + /** Idle timeout in milliseconds (default: 10000) */ + poolIdleTimeoutMs?: number; + /** Connection timeout in milliseconds (default: 0, no timeout) */ + poolConnectionTimeoutMs?: number; +} + +export const validatorHASignerConfigMappings: ConfigMappingsType = { + ...baseSignerConfigMappings, + haSigningEnabled: { + env: 'VALIDATOR_HA_SIGNING_ENABLED', + description: 'Whether HA signing / slashing protection is enabled', + ...booleanConfigHelper(false), + }, databaseUrl: { env: 'VALIDATOR_HA_DATABASE_URL', description: @@ -131,16 +146,8 @@ export function getConfigEnvVars(): ValidatorHASignerConfig { return getConfigFromMappings(validatorHASignerConfigMappings); } -export const ValidatorHASignerConfigSchema = z.object({ +export const ValidatorHASignerConfigSchema = BaseSignerConfigSchema.extend({ haSigningEnabled: z.boolean(), - l1Contracts: z.object({ - rollupAddress: z.instanceof(EthAddress), - }), - nodeId: z.string(), - pollingIntervalMs: z.number().min(0), - signingTimeoutMs: z.number().min(0), - maxStuckDutiesAgeMs: z.number().min(0).optional(), - cleanupOldDutiesAfterHours: z.number().min(0).optional(), databaseUrl: z.string().optional(), poolMaxCount: z.number().min(0).optional(), poolMinCount: z.number().min(0).optional(), diff --git a/yarn-project/stdlib/src/ha-signing/index.ts b/yarn-project/stdlib/src/ha-signing/index.ts index 7daa9c9ccdd0..4d29ef548ce3 100644 --- a/yarn-project/stdlib/src/ha-signing/index.ts +++ b/yarn-project/stdlib/src/ha-signing/index.ts @@ -1,10 +1,19 @@ export { + type BaseSignerConfig, + BaseSignerConfigSchema, + baseSignerConfigMappings, type ValidatorHASignerConfig, ValidatorHASignerConfigSchema, defaultValidatorHASignerConfig, getConfigEnvVars, validatorHASignerConfigMappings, } from './config.js'; +export { + type LocalSignerConfig, + LocalSignerConfigSchema, + getLocalSignerConfigEnvVars, + localSignerConfigMappings, +} from './local_config.js'; export { DutyType, type BlockProposalSigningContext, diff --git a/yarn-project/stdlib/src/ha-signing/local_config.ts b/yarn-project/stdlib/src/ha-signing/local_config.ts new file mode 100644 index 000000000000..48079af2b137 --- /dev/null +++ b/yarn-project/stdlib/src/ha-signing/local_config.ts @@ -0,0 +1,46 @@ +import { type ConfigMappingsType, getConfigFromMappings } from '@aztec/foundation/config'; +import { zodFor } from '@aztec/foundation/schemas'; +import { type DataStoreConfig, dataConfigMappings } from '@aztec/stdlib/kv-store'; + +import { z } from 'zod'; + +import { type BaseSignerConfig, BaseSignerConfigSchema, baseSignerConfigMappings } from './config.js'; + +/** + * Configuration for local (single-node) slashing protection. + * + * Combines the base signing protection fields (shared with HA mode) with + * DataStoreConfig for the local LMDB backing store, plus a per-store map-size + * override. Used when HA signing is disabled. + */ +export type LocalSignerConfig = BaseSignerConfig & + DataStoreConfig & { + /** Maximum size of the local signing-protection LMDB store in KB. Overwrites the general dataStoreMapSizeKb. */ + signingProtectionMapSizeKb?: number; + }; + +export const localSignerConfigMappings: ConfigMappingsType = { + ...baseSignerConfigMappings, + ...dataConfigMappings, + signingProtectionMapSizeKb: { + env: 'SIGNING_PROTECTION_MAP_SIZE_KB', + description: + 'Maximum size of the local signing-protection LMDB store in KB. Overwrites the general dataStoreMapSizeKb.', + parseEnv: (val: string | undefined) => (val ? +val : undefined), + }, +}; + +export const LocalSignerConfigSchema = zodFor()( + BaseSignerConfigSchema.extend({ + dataDirectory: z.string().optional(), + dataStoreMapSizeKb: z.number(), + signingProtectionMapSizeKb: z.number().optional(), + }), +); + +/** + * Returns the local signer configuration from environment variables. + */ +export function getLocalSignerConfigEnvVars(): LocalSignerConfig { + return getConfigFromMappings(localSignerConfigMappings); +} diff --git a/yarn-project/stdlib/src/interfaces/aztec-node-admin.test.ts b/yarn-project/stdlib/src/interfaces/aztec-node-admin.test.ts index 61014496fe1e..85384db62626 100644 --- a/yarn-project/stdlib/src/interfaces/aztec-node-admin.test.ts +++ b/yarn-project/stdlib/src/interfaces/aztec-node-admin.test.ts @@ -176,6 +176,7 @@ class MockAztecNodeAdmin implements AztecNodeAdmin { pollingIntervalMs: 50, signingTimeoutMs: 3000, maxStuckDutiesAgeMs: 72000, + dataStoreMapSizeKb: 128 * 1024 * 1024, l1Contracts: { rollupAddress: EthAddress.random(), }, diff --git a/yarn-project/stdlib/src/interfaces/validator.ts b/yarn-project/stdlib/src/interfaces/validator.ts index f0b1c05e2209..040562ce896c 100644 --- a/yarn-project/stdlib/src/interfaces/validator.ts +++ b/yarn-project/stdlib/src/interfaces/validator.ts @@ -20,58 +20,64 @@ import type { PeerId } from '@libp2p/interface'; import { z } from 'zod'; import type { CommitteeAttestationsAndSigners } from '../block/index.js'; -import { type ValidatorHASignerConfig, ValidatorHASignerConfigSchema } from '../ha-signing/index.js'; +import { + type LocalSignerConfig, + LocalSignerConfigSchema, + type ValidatorHASignerConfig, + ValidatorHASignerConfigSchema, +} from '../ha-signing/index.js'; import { AllowedElementSchema } from './allowed_element.js'; /** * Validator client configuration */ -export type ValidatorClientConfig = ValidatorHASignerConfig & { - /** The private keys of the validators participating in attestation duties */ - validatorPrivateKeys?: SecretValue<`0x${string}`[]>; +export type ValidatorClientConfig = ValidatorHASignerConfig & + LocalSignerConfig & { + /** The private keys of the validators participating in attestation duties */ + validatorPrivateKeys?: SecretValue<`0x${string}`[]>; - /** The addresses of the validators to use with remote signers */ - validatorAddresses?: EthAddress[]; + /** The addresses of the validators to use with remote signers */ + validatorAddresses?: EthAddress[]; - /** Do not run the validator */ - disableValidator: boolean; + /** Do not run the validator */ + disableValidator: boolean; - /** Temporarily disable these specific validator addresses */ - disabledValidators: EthAddress[]; + /** Temporarily disable these specific validator addresses */ + disabledValidators: EthAddress[]; - /** Interval between polling for new attestations from peers */ - attestationPollingIntervalMs: number; + /** Interval between polling for new attestations from peers */ + attestationPollingIntervalMs: number; - /** Whether to re-execute transactions in a block proposal before attesting */ - validatorReexecute: boolean; + /** Whether to re-execute transactions in a block proposal before attesting */ + validatorReexecute: boolean; - /** Whether to always reexecute block proposals, even for non-validator nodes or when out of the currnet committee */ - alwaysReexecuteBlockProposals?: boolean; + /** Whether to always reexecute block proposals, even for non-validator nodes or when out of the currnet committee */ + alwaysReexecuteBlockProposals?: boolean; - /** Whether to run in fisherman mode: validates all proposals and attestations but does not broadcast attestations or participate in consensus */ - fishermanMode?: boolean; + /** Whether to run in fisherman mode: validates all proposals and attestations but does not broadcast attestations or participate in consensus */ + fishermanMode?: boolean; - /** Skip checkpoint proposal validation and always attest (default: false) */ - skipCheckpointProposalValidation?: boolean; + /** Skip checkpoint proposal validation and always attest (default: false) */ + skipCheckpointProposalValidation?: boolean; - /** Skip pushing re-executed blocks to archiver (default: false) */ - skipPushProposedBlocksToArchiver?: boolean; + /** Skip pushing re-executed blocks to archiver (default: false) */ + skipPushProposedBlocksToArchiver?: boolean; - /** Agree to attest to equivocated checkpoint proposals (for testing purposes only) */ - attestToEquivocatedProposals?: boolean; + /** Agree to attest to equivocated checkpoint proposals (for testing purposes only) */ + attestToEquivocatedProposals?: boolean; - /** Maximum L2 gas per block for validation. Proposals exceeding this limit are rejected. */ - validateMaxL2BlockGas?: number; + /** Maximum L2 gas per block for validation. Proposals exceeding this limit are rejected. */ + validateMaxL2BlockGas?: number; - /** Maximum DA gas per block for validation. Proposals exceeding this limit are rejected. */ - validateMaxDABlockGas?: number; + /** Maximum DA gas per block for validation. Proposals exceeding this limit are rejected. */ + validateMaxDABlockGas?: number; - /** Maximum transactions per block for validation. Proposals exceeding this limit are rejected. */ - validateMaxTxsPerBlock?: number; + /** Maximum transactions per block for validation. Proposals exceeding this limit are rejected. */ + validateMaxTxsPerBlock?: number; - /** Maximum transactions per checkpoint for validation. Proposals exceeding this limit are rejected. */ - validateMaxTxsPerCheckpoint?: number; -}; + /** Maximum transactions per checkpoint for validation. Proposals exceeding this limit are rejected. */ + validateMaxTxsPerCheckpoint?: number; + }; export type ValidatorClientFullConfig = ValidatorClientConfig & Pick & @@ -87,7 +93,7 @@ export type ValidatorClientFullConfig = ValidatorClientConfig & }; export const ValidatorClientConfigSchema = zodFor>()( - ValidatorHASignerConfigSchema.extend({ + ValidatorHASignerConfigSchema.merge(LocalSignerConfigSchema).extend({ validatorAddresses: z.array(schemas.EthAddress).optional(), disableValidator: z.boolean(), disabledValidators: z.array(schemas.EthAddress), diff --git a/yarn-project/kv-store/src/config.ts b/yarn-project/stdlib/src/kv-store/config.ts similarity index 97% rename from yarn-project/kv-store/src/config.ts rename to yarn-project/stdlib/src/kv-store/config.ts index 0d09f1289fb2..46ef09e6211a 100644 --- a/yarn-project/kv-store/src/config.ts +++ b/yarn-project/stdlib/src/kv-store/config.ts @@ -3,7 +3,7 @@ import { type ConfigMappingsType, getConfigFromMappings, numberConfigHelper } fr import type { EthAddress } from '@aztec/foundation/eth-address'; export type DataStoreConfig = { - dataDirectory: string | undefined; + dataDirectory?: string; dataStoreMapSizeKb: number; l1Contracts?: { rollupAddress: EthAddress }; }; diff --git a/yarn-project/stdlib/src/kv-store/index.ts b/yarn-project/stdlib/src/kv-store/index.ts new file mode 100644 index 000000000000..3b3c5bf0ba45 --- /dev/null +++ b/yarn-project/stdlib/src/kv-store/index.ts @@ -0,0 +1 @@ +export * from './config.js'; diff --git a/yarn-project/validator-client/src/config.ts b/yarn-project/validator-client/src/config.ts index 727a4d4b2648..9b36eae81838 100644 --- a/yarn-project/validator-client/src/config.ts +++ b/yarn-project/validator-client/src/config.ts @@ -6,7 +6,7 @@ import { secretValueConfigHelper, } from '@aztec/foundation/config'; import { EthAddress } from '@aztec/foundation/eth-address'; -import { validatorHASignerConfigMappings } from '@aztec/stdlib/ha-signing'; +import { localSignerConfigMappings, validatorHASignerConfigMappings } from '@aztec/stdlib/ha-signing'; import type { ValidatorClientConfig } from '@aztec/stdlib/interfaces/server'; export type { ValidatorClientConfig }; @@ -97,6 +97,7 @@ export const validatorClientConfigMappings: ConfigMappingsType (val ? parseInt(val, 10) : undefined), }, + ...localSignerConfigMappings, ...validatorHASignerConfigMappings, }; diff --git a/yarn-project/validator-client/src/validator.ha.integration.test.ts b/yarn-project/validator-client/src/validator.ha.integration.test.ts index 80c7bd532974..1bf1fbd5229b 100644 --- a/yarn-project/validator-client/src/validator.ha.integration.test.ts +++ b/yarn-project/validator-client/src/validator.ha.integration.test.ts @@ -140,6 +140,7 @@ describe('ValidatorClient HA Integration', () => { signingTimeoutMs: 3000, maxStuckDutiesAgeMs: 72000, databaseUrl: 'postgresql://test', + dataStoreMapSizeKb: 128 * 1024 * 1024, }; // Create 5 validator nodes with unique node IDs diff --git a/yarn-project/validator-client/src/validator.integration.test.ts b/yarn-project/validator-client/src/validator.integration.test.ts index 967f3da9e042..58a2520fbf3b 100644 --- a/yarn-project/validator-client/src/validator.integration.test.ts +++ b/yarn-project/validator-client/src/validator.integration.test.ts @@ -175,6 +175,7 @@ describe('ValidatorClient Integration', () => { haSigningEnabled: false, skipCheckpointProposalValidation: false, skipPushProposedBlocksToArchiver: false, + dataStoreMapSizeKb: 128 * 1024, nodeId: 'test-node', pollingIntervalMs: 100, signingTimeoutMs: 3000, diff --git a/yarn-project/validator-client/src/validator.test.ts b/yarn-project/validator-client/src/validator.test.ts index 14799f855c4b..a8ad58ee8b9f 100644 --- a/yarn-project/validator-client/src/validator.test.ts +++ b/yarn-project/validator-client/src/validator.test.ts @@ -154,6 +154,7 @@ describe('ValidatorClient', () => { pollingIntervalMs: 1000, signingTimeoutMs: 1000, maxStuckDutiesAgeMs: 72000, + dataStoreMapSizeKb: 1024 * 1024, }; keyStoreManager = new KeystoreManager(makeKeyStore({ attester: validatorPrivateKeys.map(key => key as Hex<32>) })); @@ -1045,7 +1046,7 @@ describe('ValidatorClient', () => { it('should preserve HA signer and wrap new adapter in HAKeyStore after reload', () => { // Simulate HA mode by setting the haSigner and wrapping in HAKeyStore const mockHASigner = { nodeId: 'test-ha-node' }; - (validatorClient as any).haSigner = mockHASigner; + (validatorClient as any).slashingProtectionSigner = mockHASigner; (validatorClient as any).keyStore = haKeyStore; const newCoinbase = EthAddress.random(); diff --git a/yarn-project/validator-client/src/validator.ts b/yarn-project/validator-client/src/validator.ts index f60f2277eac7..baec6d4f2317 100644 --- a/yarn-project/validator-client/src/validator.ts +++ b/yarn-project/validator-client/src/validator.ts @@ -46,7 +46,7 @@ import type { CheckpointHeader } from '@aztec/stdlib/rollup'; import type { BlockHeader, CheckpointGlobalVariables, Tx } from '@aztec/stdlib/tx'; import { AttestationTimeoutError } from '@aztec/stdlib/validators'; import { type TelemetryClient, type Tracer, getTelemetryClient } from '@aztec/telemetry-client'; -import { createHASigner } from '@aztec/validator-ha-signer/factory'; +import { createHASigner, createLocalSignerWithProtection } from '@aztec/validator-ha-signer/factory'; import { DutyType, type SigningContext } from '@aztec/validator-ha-signer/types'; import type { ValidatorHASigner } from '@aztec/validator-ha-signer/validator-ha-signer'; @@ -109,7 +109,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter) private l1ToL2MessageSource: L1ToL2MessageSource, private config: ValidatorClientFullConfig, private blobClient: BlobClientInterface, - private haSigner: ValidatorHASigner | undefined, + private slashingProtectionSigner: ValidatorHASigner, private dateProvider: DateProvider = new DateProvider(), telemetry: TelemetryClient = getTelemetryClient(), log = createLogger('validator'), @@ -218,18 +218,27 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter) ); const nodeKeystoreAdapter = NodeKeystoreAdapter.fromKeyStoreManager(keyStoreManager); - let validatorKeyStore: ExtendedValidatorKeyStore = nodeKeystoreAdapter; - let haSigner: ValidatorHASigner | undefined; + let slashingProtectionSigner: ValidatorHASigner; if (config.haSigningEnabled) { + // Multi-node HA mode: use PostgreSQL-backed distributed locking. // If maxStuckDutiesAgeMs is not explicitly set, compute it from Aztec slot duration const haConfig = { ...config, maxStuckDutiesAgeMs: config.maxStuckDutiesAgeMs ?? epochCache.getL1Constants().slotDuration * 2 * 1000, }; - const { signer } = await createHASigner(haConfig, { telemetryClient: telemetry, dateProvider }); - haSigner = signer; - validatorKeyStore = new HAKeyStore(nodeKeystoreAdapter, signer); + ({ signer: slashingProtectionSigner } = await createHASigner(haConfig, { + telemetryClient: telemetry, + dateProvider, + })); + } else { + // Single-node mode: use LMDB-backed local signing protection. + // This prevents double-signing if the node crashes and restarts mid-proposal. + ({ signer: slashingProtectionSigner } = await createLocalSignerWithProtection(config, { + telemetryClient: telemetry, + dateProvider, + })); } + const validatorKeyStore: ExtendedValidatorKeyStore = new HAKeyStore(nodeKeystoreAdapter, slashingProtectionSigner); const validator = new ValidatorClient( validatorKeyStore, @@ -242,7 +251,7 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter) l1ToL2MessageSource, config, blobClient, - haSigner, + slashingProtectionSigner, dateProvider, telemetry, ); @@ -281,24 +290,8 @@ export class ValidatorClient extends (EventEmitter as new () => WatcherEmitter) } public reloadKeystore(newManager: KeystoreManager): void { - if (this.config.haSigningEnabled && !this.haSigner) { - this.log.warn( - 'HA signing is enabled in config but was not initialized at startup. ' + - 'Restart the node to enable HA signing.', - ); - } else if (!this.config.haSigningEnabled && this.haSigner) { - this.log.warn( - 'HA signing was disabled via config update but the HA signer is still active. ' + - 'Restart the node to fully disable HA signing.', - ); - } - const newAdapter = NodeKeystoreAdapter.fromKeyStoreManager(newManager); - if (this.haSigner) { - this.keyStore = new HAKeyStore(newAdapter, this.haSigner); - } else { - this.keyStore = newAdapter; - } + this.keyStore = new HAKeyStore(newAdapter, this.slashingProtectionSigner); this.validationService = new ValidationService(this.keyStore, this.log.createChild('validation-service')); } diff --git a/yarn-project/validator-ha-signer/README.md b/yarn-project/validator-ha-signer/README.md index 3969f709e34f..3ff3fafafae5 100644 --- a/yarn-project/validator-ha-signer/README.md +++ b/yarn-project/validator-ha-signer/README.md @@ -36,7 +36,6 @@ import { createHASigner } from '@aztec/validator-ha-signer/factory'; const { signer, db } = await createHASigner({ databaseUrl: process.env.DATABASE_URL, - haSigningEnabled: true, nodeId: 'validator-node-1', pollingIntervalMs: 100, signingTimeoutMs: 3000, @@ -81,7 +80,6 @@ const db = new PostgresSlashingProtectionDatabase(pool); await db.initialize(); const signer = new ValidatorHASigner(db, { - haSigningEnabled: true, nodeId: 'validator-node-1', pollingIntervalMs: 100, signingTimeoutMs: 3000, diff --git a/yarn-project/validator-ha-signer/package.json b/yarn-project/validator-ha-signer/package.json index 0b9a575c21ac..fbbb272e6f9d 100644 --- a/yarn-project/validator-ha-signer/package.json +++ b/yarn-project/validator-ha-signer/package.json @@ -11,7 +11,8 @@ "./slashing-protection-service": "./dest/slashing_protection_service.js", "./types": "./dest/types.js", "./validator-ha-signer": "./dest/validator_ha_signer.js", - "./test": "./dest/test/pglite_pool.js" + "./test": "./dest/test/pglite_pool.js", + "./db/lmdb": "./dest/db/lmdb.js" }, "typedocOptions": { "entryPoints": [ @@ -76,6 +77,7 @@ "dependencies": { "@aztec/ethereum": "workspace:^", "@aztec/foundation": "workspace:^", + "@aztec/kv-store": "workspace:^", "@aztec/stdlib": "workspace:^", "@aztec/telemetry-client": "workspace:^", "node-pg-migrate": "^8.0.4", diff --git a/yarn-project/validator-ha-signer/src/db/index.ts b/yarn-project/validator-ha-signer/src/db/index.ts index ea026be796cb..a8cd48407152 100644 --- a/yarn-project/validator-ha-signer/src/db/index.ts +++ b/yarn-project/validator-ha-signer/src/db/index.ts @@ -1,3 +1,4 @@ export * from './types.js'; export * from './schema.js'; export * from './postgres.js'; +export * from './lmdb.js'; diff --git a/yarn-project/validator-ha-signer/src/db/lmdb.test.ts b/yarn-project/validator-ha-signer/src/db/lmdb.test.ts new file mode 100644 index 000000000000..c7ad2a9cb5fe --- /dev/null +++ b/yarn-project/validator-ha-signer/src/db/lmdb.test.ts @@ -0,0 +1,417 @@ +import { BlockNumber, IndexWithinCheckpoint, SlotNumber } from '@aztec/foundation/branded-types'; +import { EthAddress } from '@aztec/foundation/eth-address'; +import { TestDateProvider } from '@aztec/foundation/timer'; +import { type AztecLMDBStoreV2, openStoreAt, openTmpStore } from '@aztec/kv-store/lmdb-v2'; + +import { afterEach, beforeEach, describe, expect, it } from '@jest/globals'; +import { mkdir, mkdtemp, rm } from 'fs/promises'; +import { tmpdir } from 'os'; +import { join } from 'path'; + +import { LmdbSlashingProtectionDatabase } from './lmdb.js'; +import { DutyStatus, DutyType } from './types.js'; + +describe('LmdbSlashingProtectionDatabase', () => { + let store: AztecLMDBStoreV2; + let db: LmdbSlashingProtectionDatabase; + let dateProvider: TestDateProvider; + + const ROLLUP_ADDRESS = EthAddress.random(); + const VALIDATOR_ADDRESS = EthAddress.random(); + const SLOT = SlotNumber(100); + const BLOCK_NUMBER = BlockNumber(50); + const BLOCK_INDEX = IndexWithinCheckpoint(0); + const DUTY_TYPE = DutyType.BLOCK_PROPOSAL; + const MESSAGE_HASH = '0xdeadbeef'; + const NODE_ID = 'local'; + const SIGNATURE = '0xsignature'; + + const defaultParams = () => ({ + rollupAddress: ROLLUP_ADDRESS, + validatorAddress: VALIDATOR_ADDRESS, + slot: SLOT, + blockNumber: BLOCK_NUMBER, + blockIndexWithinCheckpoint: BLOCK_INDEX, + dutyType: DUTY_TYPE, + messageHash: MESSAGE_HASH, + nodeId: NODE_ID, + }); + + beforeEach(async () => { + store = await openTmpStore('lmdb-slashing-test', true); + dateProvider = new TestDateProvider(); + db = new LmdbSlashingProtectionDatabase(store, dateProvider); + }); + + afterEach(async () => { + await db.close(); + }); + + describe('tryInsertOrGetExisting', () => { + it('should insert a new duty and return isNew=true', async () => { + const result = await db.tryInsertOrGetExisting(defaultParams()); + + expect(result.isNew).toBe(true); + expect(result.record.status).toBe(DutyStatus.SIGNING); + expect(result.record.rollupAddress.equals(ROLLUP_ADDRESS)).toBe(true); + expect(result.record.validatorAddress.equals(VALIDATOR_ADDRESS)).toBe(true); + expect(result.record.slot).toBe(SLOT); + expect(result.record.dutyType).toBe(DUTY_TYPE); + expect(result.record.messageHash).toBe(MESSAGE_HASH); + expect(result.record.nodeId).toBe(NODE_ID); + expect(result.record.lockToken).toBeTruthy(); + }); + + it('should return isNew=false and existing record on duplicate', async () => { + const first = await db.tryInsertOrGetExisting(defaultParams()); + expect(first.isNew).toBe(true); + + const second = await db.tryInsertOrGetExisting({ ...defaultParams(), nodeId: 'other-node' }); + expect(second.isNew).toBe(false); + expect(second.record.nodeId).toBe(NODE_ID); + }); + + it('should strip lockToken from existing record', async () => { + const first = await db.tryInsertOrGetExisting(defaultParams()); + expect(first.isNew).toBe(true); + expect(first.record.lockToken).toBeTruthy(); + + const second = await db.tryInsertOrGetExisting({ ...defaultParams(), nodeId: 'other-node' }); + expect(second.isNew).toBe(false); + expect(second.record.lockToken).toBe(''); + }); + + it('should allow independent duties for different slots, duty types, and validators', async () => { + const [slot1, slot2] = await Promise.all([ + db.tryInsertOrGetExisting({ ...defaultParams(), slot: SlotNumber(1) }), + db.tryInsertOrGetExisting({ ...defaultParams(), slot: SlotNumber(2) }), + ]); + expect(slot1.isNew).toBe(true); + expect(slot2.isNew).toBe(true); + + const [proposalResult, attestResult] = await Promise.all([ + db.tryInsertOrGetExisting({ ...defaultParams(), dutyType: DutyType.BLOCK_PROPOSAL }), + db.tryInsertOrGetExisting({ + rollupAddress: ROLLUP_ADDRESS, + validatorAddress: VALIDATOR_ADDRESS, + slot: SLOT, + blockNumber: BLOCK_NUMBER, + dutyType: DutyType.ATTESTATION, + messageHash: MESSAGE_HASH, + nodeId: NODE_ID, + }), + ]); + expect(proposalResult.isNew).toBe(true); + expect(attestResult.isNew).toBe(true); + + const [v1, v2] = await Promise.all([ + db.tryInsertOrGetExisting({ ...defaultParams(), validatorAddress: EthAddress.random() }), + db.tryInsertOrGetExisting({ ...defaultParams(), validatorAddress: EthAddress.random() }), + ]); + expect(v1.isNew).toBe(true); + expect(v2.isNew).toBe(true); + }); + }); + + describe('updateDutySigned', () => { + it('should update duty to signed status with correct lockToken', async () => { + const { record } = await db.tryInsertOrGetExisting(defaultParams()); + + const success = await db.updateDutySigned( + ROLLUP_ADDRESS, + VALIDATOR_ADDRESS, + SLOT, + DUTY_TYPE, + SIGNATURE, + record.lockToken, + BLOCK_INDEX, + ); + + expect(success).toBe(true); + + // Subsequent insert attempt returns existing record with SIGNED status + const second = await db.tryInsertOrGetExisting(defaultParams()); + expect(second.isNew).toBe(false); + expect(second.record.status).toBe(DutyStatus.SIGNED); + expect(second.record.signature).toBe(SIGNATURE); + }); + + it('should return false when lockToken does not match', async () => { + await db.tryInsertOrGetExisting(defaultParams()); + + const success = await db.updateDutySigned( + ROLLUP_ADDRESS, + VALIDATOR_ADDRESS, + SLOT, + DUTY_TYPE, + SIGNATURE, + 'wrong-token', + BLOCK_INDEX, + ); + + expect(success).toBe(false); + }); + + it('should return false when duty does not exist', async () => { + const success = await db.updateDutySigned( + ROLLUP_ADDRESS, + VALIDATOR_ADDRESS, + SLOT, + DUTY_TYPE, + SIGNATURE, + 'any-token', + BLOCK_INDEX, + ); + + expect(success).toBe(false); + }); + }); + + describe('deleteDuty', () => { + it('should delete duty with correct lockToken', async () => { + const { record } = await db.tryInsertOrGetExisting(defaultParams()); + + const success = await db.deleteDuty( + ROLLUP_ADDRESS, + VALIDATOR_ADDRESS, + SLOT, + DUTY_TYPE, + record.lockToken, + BLOCK_INDEX, + ); + + expect(success).toBe(true); + + // Should now be insertable again + const retry = await db.tryInsertOrGetExisting(defaultParams()); + expect(retry.isNew).toBe(true); + }); + + it('should return false when lockToken does not match', async () => { + await db.tryInsertOrGetExisting(defaultParams()); + + const success = await db.deleteDuty( + ROLLUP_ADDRESS, + VALIDATOR_ADDRESS, + SLOT, + DUTY_TYPE, + 'wrong-token', + BLOCK_INDEX, + ); + + expect(success).toBe(false); + }); + + it('should return false when duty does not exist', async () => { + const success = await db.deleteDuty(ROLLUP_ADDRESS, VALIDATOR_ADDRESS, SLOT, DUTY_TYPE, 'any-token', BLOCK_INDEX); + + expect(success).toBe(false); + }); + }); + + describe('cleanupOwnStuckDuties', () => { + it('should remove stuck SIGNING duties older than maxAgeMs', async () => { + await db.tryInsertOrGetExisting(defaultParams()); + + // Advance past the maxAge threshold + dateProvider.advanceTime(120); + const count = await db.cleanupOwnStuckDuties(NODE_ID, 60_000); + expect(count).toBe(1); + + // Should now be insertable again (duty was deleted) + const retry = await db.tryInsertOrGetExisting(defaultParams()); + expect(retry.isNew).toBe(true); + }); + + it('should not remove duties for other node IDs', async () => { + await db.tryInsertOrGetExisting(defaultParams()); + + dateProvider.advanceTime(120); + const count = await db.cleanupOwnStuckDuties('different-node', 60_000); + expect(count).toBe(0); + }); + + it('should not remove SIGNED duties', async () => { + const { record } = await db.tryInsertOrGetExisting(defaultParams()); + await db.updateDutySigned( + ROLLUP_ADDRESS, + VALIDATOR_ADDRESS, + SLOT, + DUTY_TYPE, + SIGNATURE, + record.lockToken, + BLOCK_INDEX, + ); + + dateProvider.advanceTime(120); + const count = await db.cleanupOwnStuckDuties(NODE_ID, 60_000); + expect(count).toBe(0); + }); + + it('should not remove fresh SIGNING duties within maxAgeMs', async () => { + await db.tryInsertOrGetExisting(defaultParams()); + + dateProvider.advanceTime(30); + const count = await db.cleanupOwnStuckDuties(NODE_ID, 60_000); + expect(count).toBe(0); + }); + }); + + describe('cleanupOutdatedRollupDuties', () => { + it('is always a no-op: rollup address changes are handled at startup by DatabaseVersionManager', async () => { + await db.tryInsertOrGetExisting(defaultParams()); + + const differentRollup = EthAddress.random(); + const count = await db.cleanupOutdatedRollupDuties(differentRollup); + expect(count).toBe(0); + }); + }); + + describe('cleanupOldDuties', () => { + it('should remove old SIGNED duties', async () => { + const { record } = await db.tryInsertOrGetExisting(defaultParams()); + await db.updateDutySigned( + ROLLUP_ADDRESS, + VALIDATOR_ADDRESS, + SLOT, + DUTY_TYPE, + SIGNATURE, + record.lockToken, + BLOCK_INDEX, + ); + + dateProvider.advanceTime(120); + const count = await db.cleanupOldDuties(60_000); + expect(count).toBe(1); + }); + + it('should not remove SIGNING duties', async () => { + await db.tryInsertOrGetExisting(defaultParams()); + + dateProvider.advanceTime(120); + const count = await db.cleanupOldDuties(60_000); + expect(count).toBe(0); + }); + + it('should not remove fresh SIGNED duties within maxAgeMs', async () => { + const { record } = await db.tryInsertOrGetExisting(defaultParams()); + await db.updateDutySigned( + ROLLUP_ADDRESS, + VALIDATOR_ADDRESS, + SLOT, + DUTY_TYPE, + SIGNATURE, + record.lockToken, + BLOCK_INDEX, + ); + + dateProvider.advanceTime(30); + const count = await db.cleanupOldDuties(60_000); + expect(count).toBe(0); + }); + }); +}); + +/** + * Restart-persistence tests. + * + * These tests verify the core motivation for the local LMDB slashing protection: + * a sequencer that sends a block/checkpoint proposal and then restarts must NOT + * send a duplicate proposal for the same slot. + */ +describe('LmdbSlashingProtectionDatabase - persistence across restarts', () => { + const ROLLUP_ADDRESS = EthAddress.random(); + const VALIDATOR_ADDRESS = EthAddress.random(); + const SLOT = SlotNumber(100); + const BLOCK_NUMBER = BlockNumber(50); + const BLOCK_INDEX = IndexWithinCheckpoint(0); + const DUTY_TYPE = DutyType.BLOCK_PROPOSAL; + const MESSAGE_HASH = '0xdeadbeef'; + const NODE_ID = 'local'; + const SIGNATURE = '0xsignature'; + + const defaultParams = () => ({ + rollupAddress: ROLLUP_ADDRESS, + validatorAddress: VALIDATOR_ADDRESS, + slot: SLOT, + blockNumber: BLOCK_NUMBER, + blockIndexWithinCheckpoint: BLOCK_INDEX, + dutyType: DUTY_TYPE, + messageHash: MESSAGE_HASH, + nodeId: NODE_ID, + }); + + let dataDir: string; + let dateProvider: TestDateProvider; + + beforeEach(async () => { + dataDir = await mkdtemp(join(tmpdir(), 'lmdb-slashing-restart-')); + await mkdir(dataDir, { recursive: true }); + dateProvider = new TestDateProvider(); + }); + + afterEach(async () => { + await rm(dataDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 }); + }); + + const openDb = async () => { + const store = await openStoreAt(dataDir); + return { store, db: new LmdbSlashingProtectionDatabase(store, dateProvider) }; + }; + + it('should block duplicate block proposals after a node restart', async () => { + // First run: node signs a block proposal and records it successfully. + const { db: db1 } = await openDb(); + const { record } = await db1.tryInsertOrGetExisting(defaultParams()); + await db1.updateDutySigned( + ROLLUP_ADDRESS, + VALIDATOR_ADDRESS, + SLOT, + DUTY_TYPE, + SIGNATURE, + record.lockToken, + BLOCK_INDEX, + ); + await db1.close(); + + // Restart: reopen the same store. + const { db: db2 } = await openDb(); + try { + const result = await db2.tryInsertOrGetExisting(defaultParams()); + + // The existing SIGNED record is returned; the node must not sign again. + expect(result.isNew).toBe(false); + expect(result.record.status).toBe(DutyStatus.SIGNED); + expect(result.record.signature).toBe(SIGNATURE); + } finally { + await db2.close(); + } + }); + + it('should allow re-signing after crash-cleanup of a stuck SIGNING duty', async () => { + // First run: node starts signing but crashes before completing. The lockToken + // held in memory is lost; the duty is left in SIGNING state on disk. + const { db: db1 } = await openDb(); + await db1.tryInsertOrGetExisting(defaultParams()); + await db1.close(); // crash - lockToken is lost + + // Restart: the stuck SIGNING duty is visible on disk. + dateProvider.advanceTime(120); + const { db: db2 } = await openDb(); + try { + const stuck = await db2.tryInsertOrGetExisting(defaultParams()); + expect(stuck.isNew).toBe(false); + expect(stuck.record.status).toBe(DutyStatus.SIGNING); + + // On startup, the node cleans up its own stuck duties + const cleaned = await db2.cleanupOwnStuckDuties(NODE_ID, 60_000); + expect(cleaned).toBe(1); + + // The duty is gone; the node can now safely re-attempt the signing. + const retry = await db2.tryInsertOrGetExisting(defaultParams()); + expect(retry.isNew).toBe(true); + } finally { + await db2.close(); + } + }); +}); diff --git a/yarn-project/validator-ha-signer/src/db/lmdb.ts b/yarn-project/validator-ha-signer/src/db/lmdb.ts new file mode 100644 index 000000000000..1a8c8c9a8f90 --- /dev/null +++ b/yarn-project/validator-ha-signer/src/db/lmdb.ts @@ -0,0 +1,264 @@ +/** + * LMDB implementation of SlashingProtectionDatabase + * + * Provides local (single-node) double-signing protection using LMDB as the backend. + * Suitable for nodes that do NOT run in a high-availability multi-node setup. + * + * The LMDB store is single-writer, making setIfNotExists inherently atomic. + * This means we get crash-restart protection without needing an external database. + */ +import { SlotNumber } from '@aztec/foundation/branded-types'; +import { randomBytes } from '@aztec/foundation/crypto/random'; +import { EthAddress } from '@aztec/foundation/eth-address'; +import { type Logger, createLogger } from '@aztec/foundation/log'; +import type { DateProvider } from '@aztec/foundation/timer'; +import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store'; + +import type { SlashingProtectionDatabase, TryInsertOrGetResult } from '../types.js'; +import { + type CheckAndRecordParams, + DutyStatus, + DutyType, + type StoredDutyRecord, + getBlockIndexFromDutyIdentifier, + recordFromFields, +} from './types.js'; + +function dutyKey( + rollupAddress: string, + validatorAddress: string, + slot: string, + dutyType: string, + blockIndexWithinCheckpoint: number, +): string { + return `${rollupAddress}:${validatorAddress}:${slot}:${dutyType}:${blockIndexWithinCheckpoint}`; +} + +/** + * LMDB-backed implementation of SlashingProtectionDatabase. + * + * Provides single-node double-signing protection that survives crashes and restarts. + * Does not provide cross-node coordination (that requires the PostgreSQL implementation). + */ +export class LmdbSlashingProtectionDatabase implements SlashingProtectionDatabase { + public static readonly SCHEMA_VERSION = 1; + + private readonly duties: AztecAsyncMap; + private readonly log: Logger; + + constructor( + private readonly store: AztecAsyncKVStore, + private readonly dateProvider: DateProvider, + ) { + this.log = createLogger('slashing-protection:lmdb'); + this.duties = store.openMap('signing-protection-duties'); + } + + /** + * Atomically try to insert a new duty record, or get the existing one if present. + * + * LMDB is single-writer so the read-then-write inside transactionAsync is naturally atomic. + */ + public async tryInsertOrGetExisting(params: CheckAndRecordParams): Promise { + const blockIndexWithinCheckpoint = getBlockIndexFromDutyIdentifier(params); + const key = dutyKey( + params.rollupAddress.toString(), + params.validatorAddress.toString(), + params.slot.toString(), + params.dutyType, + blockIndexWithinCheckpoint, + ); + + const lockToken = randomBytes(16).toString('hex'); + const now = this.dateProvider.now(); + + const result = await this.store.transactionAsync(async () => { + const existing = await this.duties.getAsync(key); + if (existing) { + return { isNew: false as const, record: { ...existing, lockToken: '' } }; + } + + const newRecord: StoredDutyRecord = { + rollupAddress: params.rollupAddress.toString(), + validatorAddress: params.validatorAddress.toString(), + slot: params.slot.toString(), + blockNumber: params.blockNumber.toString(), + blockIndexWithinCheckpoint, + dutyType: params.dutyType, + status: DutyStatus.SIGNING, + messageHash: params.messageHash, + nodeId: params.nodeId, + lockToken, + startedAtMs: now, + }; + await this.duties.set(key, newRecord); + return { isNew: true as const, record: newRecord }; + }); + + if (result.isNew) { + this.log.debug(`Acquired lock for duty ${params.dutyType} at slot ${params.slot}`, { + validatorAddress: params.validatorAddress.toString(), + nodeId: params.nodeId, + }); + } + + return { isNew: result.isNew, record: recordFromFields(result.record) }; + } + + /** + * Update a duty to 'signed' status with the signature. + * Only succeeds if the lockToken matches. + */ + public updateDutySigned( + rollupAddress: EthAddress, + validatorAddress: EthAddress, + slot: SlotNumber, + dutyType: DutyType, + signature: string, + lockToken: string, + blockIndexWithinCheckpoint: number, + ): Promise { + const key = dutyKey( + rollupAddress.toString(), + validatorAddress.toString(), + slot.toString(), + dutyType, + blockIndexWithinCheckpoint, + ); + + return this.store.transactionAsync(async () => { + const existing = await this.duties.getAsync(key); + if (!existing) { + this.log.warn('Failed to update duty to signed: duty not found', { + rollupAddress: rollupAddress.toString(), + validatorAddress: validatorAddress.toString(), + slot: slot.toString(), + dutyType, + blockIndexWithinCheckpoint, + }); + return false; + } + + if (existing.lockToken !== lockToken) { + this.log.warn('Failed to update duty to signed: invalid token', { + rollupAddress: rollupAddress.toString(), + validatorAddress: validatorAddress.toString(), + slot: slot.toString(), + dutyType, + blockIndexWithinCheckpoint, + }); + return false; + } + + await this.duties.set(key, { + ...existing, + status: DutyStatus.SIGNED, + signature, + completedAtMs: this.dateProvider.now(), + }); + + return true; + }); + } + + /** + * Delete a duty record. + * Only succeeds if the lockToken matches. + */ + public deleteDuty( + rollupAddress: EthAddress, + validatorAddress: EthAddress, + slot: SlotNumber, + dutyType: DutyType, + lockToken: string, + blockIndexWithinCheckpoint: number, + ): Promise { + const key = dutyKey( + rollupAddress.toString(), + validatorAddress.toString(), + slot.toString(), + dutyType, + blockIndexWithinCheckpoint, + ); + + return this.store.transactionAsync(async () => { + const existing = await this.duties.getAsync(key); + if (!existing || existing.lockToken !== lockToken) { + this.log.warn('Failed to delete duty: invalid token or duty not found', { + rollupAddress: rollupAddress.toString(), + validatorAddress: validatorAddress.toString(), + slot: slot.toString(), + dutyType, + blockIndexWithinCheckpoint, + }); + return false; + } + + await this.duties.delete(key); + return true; + }); + } + + /** + * Cleanup own stuck duties (SIGNING status older than maxAgeMs). + */ + public cleanupOwnStuckDuties(nodeId: string, maxAgeMs: number): Promise { + const cutoffMs = this.dateProvider.now() - maxAgeMs; + + return this.store.transactionAsync(async () => { + const keysToDelete: string[] = []; + for await (const [key, record] of this.duties.entriesAsync()) { + if (record.nodeId === nodeId && record.status === DutyStatus.SIGNING && record.startedAtMs < cutoffMs) { + keysToDelete.push(key); + } + } + for (const key of keysToDelete) { + await this.duties.delete(key); + } + return keysToDelete.length; + }); + } + + /** + * Cleanup duties with outdated rollup address. + * + * This is always a no-op for the LMDB implementation: the underlying store is created via + * DatabaseVersionManager (in factory.ts), which already resets the entire data directory at + * startup whenever the rollup address changes. + */ + public cleanupOutdatedRollupDuties(_currentRollupAddress: EthAddress): Promise { + return Promise.resolve(0); + } + + /** + * Cleanup old signed duties older than maxAgeMs. + */ + public cleanupOldDuties(maxAgeMs: number): Promise { + const cutoffMs = this.dateProvider.now() - maxAgeMs; + + return this.store.transactionAsync(async () => { + const keysToDelete: string[] = []; + for await (const [key, record] of this.duties.entriesAsync()) { + if ( + record.status === DutyStatus.SIGNED && + record.completedAtMs !== undefined && + record.completedAtMs < cutoffMs + ) { + keysToDelete.push(key); + } + } + for (const key of keysToDelete) { + await this.duties.delete(key); + } + return keysToDelete.length; + }); + } + + /** + * Close the underlying LMDB store. + */ + public async close(): Promise { + await this.store.close(); + this.log.debug('LMDB slashing protection database closed'); + } +} diff --git a/yarn-project/validator-ha-signer/src/db/postgres.ts b/yarn-project/validator-ha-signer/src/db/postgres.ts index 8c80f22ddd7b..d0d1c52dfa60 100644 --- a/yarn-project/validator-ha-signer/src/db/postgres.ts +++ b/yarn-project/validator-ha-signer/src/db/postgres.ts @@ -1,7 +1,7 @@ /** * PostgreSQL implementation of SlashingProtectionDatabase */ -import { BlockNumber, SlotNumber } from '@aztec/foundation/branded-types'; +import { SlotNumber } from '@aztec/foundation/branded-types'; import { randomBytes } from '@aztec/foundation/crypto/random'; import { EthAddress } from '@aztec/foundation/eth-address'; import { type Logger, createLogger } from '@aztec/foundation/log'; @@ -20,7 +20,7 @@ import { UPDATE_DUTY_SIGNED, } from './schema.js'; import type { CheckAndRecordParams, DutyRow, DutyType, InsertOrGetRow, ValidatorDutyRecord } from './types.js'; -import { getBlockIndexFromDutyIdentifier } from './types.js'; +import { getBlockIndexFromDutyIdentifier, recordFromFields } from './types.js'; /** * Minimal pool interface for database operations. @@ -220,14 +220,16 @@ export class PostgresSlashingProtectionDatabase implements SlashingProtectionDat } /** - * Convert a database row to a ValidatorDutyRecord + * Convert a database row to a ValidatorDutyRecord. + * Maps snake_case column names to StoredDutyRecord (camelCase, ms timestamps), + * then delegates to the shared recordFromFields() converter. */ private rowToRecord(row: DutyRow): ValidatorDutyRecord { - return { - rollupAddress: EthAddress.fromString(row.rollup_address), - validatorAddress: EthAddress.fromString(row.validator_address), - slot: SlotNumber.fromString(row.slot), - blockNumber: BlockNumber.fromString(row.block_number), + return recordFromFields({ + rollupAddress: row.rollup_address, + validatorAddress: row.validator_address, + slot: row.slot, + blockNumber: row.block_number, blockIndexWithinCheckpoint: row.block_index_within_checkpoint, dutyType: row.duty_type, status: row.status, @@ -235,10 +237,10 @@ export class PostgresSlashingProtectionDatabase implements SlashingProtectionDat signature: row.signature ?? undefined, nodeId: row.node_id, lockToken: row.lock_token, - startedAt: row.started_at, - completedAt: row.completed_at ?? undefined, + startedAtMs: row.started_at.getTime(), + completedAtMs: row.completed_at?.getTime(), errorMessage: row.error_message ?? undefined, - }; + }); } /** diff --git a/yarn-project/validator-ha-signer/src/db/types.ts b/yarn-project/validator-ha-signer/src/db/types.ts index b97ff2de188d..8b0572cab924 100644 --- a/yarn-project/validator-ha-signer/src/db/types.ts +++ b/yarn-project/validator-ha-signer/src/db/types.ts @@ -1,5 +1,10 @@ -import type { BlockNumber, CheckpointNumber, IndexWithinCheckpoint, SlotNumber } from '@aztec/foundation/branded-types'; -import type { EthAddress } from '@aztec/foundation/eth-address'; +import { + BlockNumber, + type CheckpointNumber, + type IndexWithinCheckpoint, + SlotNumber, +} from '@aztec/foundation/branded-types'; +import { EthAddress } from '@aztec/foundation/eth-address'; import type { Signature } from '@aztec/foundation/eth-signature'; import { DutyType } from '@aztec/stdlib/ha-signing'; @@ -23,6 +28,30 @@ export interface DutyRow { error_message: string | null; } +/** + * Plain-primitive representation of a duty record suitable for serialization + * (e.g. msgpackr for LMDB). All domain types are stored as their string/number + * equivalents. Timestamps are Unix milliseconds. + */ +export interface StoredDutyRecord { + rollupAddress: string; + validatorAddress: string; + slot: string; + blockNumber: string; + blockIndexWithinCheckpoint: number; + dutyType: DutyType; + status: DutyStatus; + messageHash: string; + signature?: string; + nodeId: string; + lockToken: string; + /** Unix timestamp in milliseconds when signing started */ + startedAtMs: number; + /** Unix timestamp in milliseconds when signing completed */ + completedAtMs?: number; + errorMessage?: string; +} + /** * Row type from INSERT_OR_GET_DUTY query (includes is_new flag) */ @@ -42,7 +71,8 @@ export enum DutyStatus { export { DutyType }; /** - * Record of a validator duty in the database + * Rich representation of a validator duty, with branded types and Date objects. + * This is the common output type returned by all SlashingProtectionDatabase implementations. */ export interface ValidatorDutyRecord { /** Ethereum address of the rollup contract */ @@ -75,6 +105,31 @@ export interface ValidatorDutyRecord { errorMessage?: string; } +/** + * Convert a {@link StoredDutyRecord} (plain-primitive wire format) to a + * {@link ValidatorDutyRecord} (rich domain type). + * + * Shared by LMDB and any future non-Postgres backend implementations. + */ +export function recordFromFields(stored: StoredDutyRecord): ValidatorDutyRecord { + return { + rollupAddress: EthAddress.fromString(stored.rollupAddress), + validatorAddress: EthAddress.fromString(stored.validatorAddress), + slot: SlotNumber.fromString(stored.slot), + blockNumber: BlockNumber.fromString(stored.blockNumber), + blockIndexWithinCheckpoint: stored.blockIndexWithinCheckpoint, + dutyType: stored.dutyType, + status: stored.status, + messageHash: stored.messageHash, + signature: stored.signature, + nodeId: stored.nodeId, + lockToken: stored.lockToken, + startedAt: new Date(stored.startedAtMs), + completedAt: stored.completedAtMs !== undefined ? new Date(stored.completedAtMs) : undefined, + errorMessage: stored.errorMessage, + }; +} + /** * Duty identifier for block proposals. * blockIndexWithinCheckpoint is REQUIRED and must be >= 0. diff --git a/yarn-project/validator-ha-signer/src/factory.ts b/yarn-project/validator-ha-signer/src/factory.ts index db52dacf05ff..3f9e09e69f00 100644 --- a/yarn-project/validator-ha-signer/src/factory.ts +++ b/yarn-project/validator-ha-signer/src/factory.ts @@ -2,14 +2,16 @@ * Factory functions for creating validator HA signers */ import { DateProvider } from '@aztec/foundation/timer'; -import type { ValidatorHASignerConfig } from '@aztec/stdlib/ha-signing'; +import { createStore } from '@aztec/kv-store/lmdb-v2'; +import type { LocalSignerConfig, ValidatorHASignerConfig } from '@aztec/stdlib/ha-signing'; import { getTelemetryClient } from '@aztec/telemetry-client'; import { Pool } from 'pg'; +import { LmdbSlashingProtectionDatabase } from './db/lmdb.js'; import { PostgresSlashingProtectionDatabase } from './db/postgres.js'; import { HASignerMetrics } from './metrics.js'; -import type { CreateHASignerDeps, SlashingProtectionDatabase } from './types.js'; +import type { CreateHASignerDeps, CreateLocalSignerWithProtectionDeps, SlashingProtectionDatabase } from './types.js'; import { ValidatorHASigner } from './validator_ha_signer.js'; /** @@ -27,7 +29,6 @@ import { ValidatorHASigner } from './validator_ha_signer.js'; * ```typescript * const { signer, db } = await createHASigner({ * databaseUrl: process.env.DATABASE_URL, - * haSigningEnabled: true, * nodeId: 'validator-node-1', * pollingIntervalMs: 100, * signingTimeoutMs: 3000, @@ -87,7 +88,52 @@ export async function createHASigner( const metrics = new HASignerMetrics(telemetryClient, signerConfig.nodeId); // Create signer - const signer = new ValidatorHASigner(db, { ...signerConfig, databaseUrl }, { metrics, dateProvider }); + const signer = new ValidatorHASigner(db, signerConfig, { metrics, dateProvider }); + + return { signer, db }; +} + +/** + * Create a local (single-node) signing protection signer backed by LMDB. + * + * This provides double-signing protection for nodes that are NOT running in a + * high-availability (multi-node) setup. It prevents a proposer from sending two + * proposals for the same slot if the node crashes and restarts mid-proposal. + * + * When `config.dataDirectory` is set, the protection database is persisted to disk + * and survives crashes/restarts. When unset, an ephemeral in-memory store is + * used which protects within a single run but not across restarts. + * + * @param config - Local signer config + * @param deps - Optional dependencies (telemetry, date provider). + * @returns An object containing the signer and database instances. + */ +export async function createLocalSignerWithProtection( + config: LocalSignerConfig, + deps?: CreateLocalSignerWithProtectionDeps, +): Promise<{ + signer: ValidatorHASigner; + db: SlashingProtectionDatabase; +}> { + const telemetryClient = deps?.telemetryClient ?? getTelemetryClient(); + const dateProvider = deps?.dateProvider ?? new DateProvider(); + + const kvStore = await createStore('signing-protection', LmdbSlashingProtectionDatabase.SCHEMA_VERSION, { + dataDirectory: config.dataDirectory, + dataStoreMapSizeKb: config.signingProtectionMapSizeKb ?? config.dataStoreMapSizeKb, + l1Contracts: config.l1Contracts, + }); + + const db = new LmdbSlashingProtectionDatabase(kvStore, dateProvider); + + const signerConfig = { + ...config, + nodeId: config.nodeId || 'local', + }; + + const metrics = new HASignerMetrics(telemetryClient, signerConfig.nodeId, 'LocalSigningProtectionMetrics'); + + const signer = new ValidatorHASigner(db, signerConfig, { metrics, dateProvider }); return { signer, db }; } diff --git a/yarn-project/validator-ha-signer/src/slashing_protection_service.test.ts b/yarn-project/validator-ha-signer/src/slashing_protection_service.test.ts index 9a776a70ed16..2420ea6bf302 100644 --- a/yarn-project/validator-ha-signer/src/slashing_protection_service.test.ts +++ b/yarn-project/validator-ha-signer/src/slashing_protection_service.test.ts @@ -3,7 +3,7 @@ import { Buffer32 } from '@aztec/foundation/buffer'; import { EthAddress } from '@aztec/foundation/eth-address'; import { sleep } from '@aztec/foundation/sleep'; import { TestDateProvider } from '@aztec/foundation/timer'; -import { DutyType, type ValidatorHASignerConfig } from '@aztec/stdlib/ha-signing'; +import { type BaseSignerConfig, DutyType } from '@aztec/stdlib/ha-signing'; import { getTelemetryClient } from '@aztec/telemetry-client'; import { PGlite } from '@electric-sql/pglite'; @@ -36,7 +36,7 @@ describe('SlashingProtectionService', () => { let pool: Pool; let db: PostgresSlashingProtectionDatabase; let service: SlashingProtectionService; - let config: ValidatorHASignerConfig; + let config: BaseSignerConfig; let dateProvider: TestDateProvider; const telemetryClient = getTelemetryClient(); @@ -51,7 +51,6 @@ describe('SlashingProtectionService', () => { dateProvider = new TestDateProvider(); config = { - haSigningEnabled: true, l1Contracts: { rollupAddress: ROLLUP_ADDRESS }, nodeId: NODE_ID, pollingIntervalMs: 50, diff --git a/yarn-project/validator-ha-signer/src/slashing_protection_service.ts b/yarn-project/validator-ha-signer/src/slashing_protection_service.ts index 232276e8738e..ac344c1f379e 100644 --- a/yarn-project/validator-ha-signer/src/slashing_protection_service.ts +++ b/yarn-project/validator-ha-signer/src/slashing_protection_service.ts @@ -8,7 +8,7 @@ import { type Logger, createLogger } from '@aztec/foundation/log'; import { RunningPromise } from '@aztec/foundation/promise'; import { sleep } from '@aztec/foundation/sleep'; import type { DateProvider } from '@aztec/foundation/timer'; -import type { ValidatorHASignerConfig } from '@aztec/stdlib/ha-signing'; +import type { BaseSignerConfig } from '@aztec/stdlib/ha-signing'; import { type CheckAndRecordParams, @@ -55,7 +55,7 @@ export class SlashingProtectionService { constructor( private readonly db: SlashingProtectionDatabase, - private readonly config: ValidatorHASignerConfig, + private readonly config: BaseSignerConfig, deps: SlashingProtectionServiceDeps, ) { this.log = createLogger('slashing-protection'); diff --git a/yarn-project/validator-ha-signer/src/types.ts b/yarn-project/validator-ha-signer/src/types.ts index 07626bab2c64..58b2cc063031 100644 --- a/yarn-project/validator-ha-signer/src/types.ts +++ b/yarn-project/validator-ha-signer/src/types.ts @@ -70,6 +70,11 @@ export interface CreateHASignerDeps { dateProvider?: DateProvider; } +/** + * deps for creating a local signing protection signer + */ +export type CreateLocalSignerWithProtectionDeps = Omit; + /** * Database interface for slashing protection operations * This abstraction allows for different database implementations (PostgreSQL, SQLite, etc.) diff --git a/yarn-project/validator-ha-signer/src/validator_ha_signer.test.ts b/yarn-project/validator-ha-signer/src/validator_ha_signer.test.ts index 17f4f62be56b..d657afcb37cf 100644 --- a/yarn-project/validator-ha-signer/src/validator_ha_signer.test.ts +++ b/yarn-project/validator-ha-signer/src/validator_ha_signer.test.ts @@ -4,7 +4,7 @@ import { EthAddress } from '@aztec/foundation/eth-address'; import type { Signature } from '@aztec/foundation/eth-signature'; import { sleep } from '@aztec/foundation/sleep'; import { TestDateProvider } from '@aztec/foundation/timer'; -import { type ValidatorHASignerConfig, defaultValidatorHASignerConfig } from '@aztec/stdlib/ha-signing'; +import { type BaseSignerConfig, defaultValidatorHASignerConfig } from '@aztec/stdlib/ha-signing'; import { getTelemetryClient } from '@aztec/telemetry-client'; import { PGlite } from '@electric-sql/pglite'; @@ -34,7 +34,7 @@ describe('ValidatorHASigner', () => { let pglite: PGlite; let pool: Pool; let db: PostgresSlashingProtectionDatabase; - let config: ValidatorHASignerConfig; + let config: BaseSignerConfig; let dateProvider: TestDateProvider; const telemetryClient = getTelemetryClient(); @@ -49,13 +49,11 @@ describe('ValidatorHASigner', () => { dateProvider = new TestDateProvider(); config = { - haSigningEnabled: true, l1Contracts: { rollupAddress: EthAddress.random() }, nodeId: NODE_ID, pollingIntervalMs: 50, signingTimeoutMs: 1000, maxStuckDutiesAgeMs: 60_000, - databaseUrl: 'postgresql://user:pass@localhost:5432/testdb', }; }); @@ -75,25 +73,8 @@ describe('ValidatorHASigner', () => { l1Contracts: { rollupAddress: EthAddress.random() }, }; const metrics = new HASignerMetrics(telemetryClient, 'test-node'); - expect( - () => - new ValidatorHASigner( - db, - { - ...defaultConfig, - databaseUrl: 'postgresql://user:pass@localhost:5432/testdb', - haSigningEnabled: true, - }, - { metrics, dateProvider }, - ), - ).toThrow('NODE_ID is required for high-availability setups'); - }); - - it('should not initialize when enabled is false', () => { - const disabledConfig = { ...config, haSigningEnabled: false }; - const metrics = new HASignerMetrics(telemetryClient, 'test-node'); - expect(() => new ValidatorHASigner(db, disabledConfig, { metrics, dateProvider })).toThrow( - 'Validator HA Signer is not enabled in config', + expect(() => new ValidatorHASigner(db, defaultConfig, { metrics, dateProvider })).toThrow( + 'NODE_ID is required for high-availability setups', ); }); }); diff --git a/yarn-project/validator-ha-signer/src/validator_ha_signer.ts b/yarn-project/validator-ha-signer/src/validator_ha_signer.ts index 724ce2eb0b63..f16f12c53bc6 100644 --- a/yarn-project/validator-ha-signer/src/validator_ha_signer.ts +++ b/yarn-project/validator-ha-signer/src/validator_ha_signer.ts @@ -11,9 +11,9 @@ import type { Signature } from '@aztec/foundation/eth-signature'; import { type Logger, createLogger } from '@aztec/foundation/log'; import type { DateProvider } from '@aztec/foundation/timer'; import { + type BaseSignerConfig, DutyType, type HAProtectedSigningContext, - type ValidatorHASignerConfig, getBlockNumberFromSigningContext, } from '@aztec/stdlib/ha-signing'; @@ -56,7 +56,7 @@ export class ValidatorHASigner { constructor( db: SlashingProtectionDatabase, - private readonly config: ValidatorHASignerConfig, + private readonly config: BaseSignerConfig, deps: ValidatorHASignerDeps, ) { this.log = createLogger('validator-ha-signer'); @@ -64,11 +64,6 @@ export class ValidatorHASigner { this.metrics = deps.metrics; this.dateProvider = deps.dateProvider; - if (!config.haSigningEnabled) { - // this shouldn't happen, the validator should use different signer for non-HA setups - throw new Error('Validator HA Signer is not enabled in config'); - } - if (!config.nodeId || config.nodeId === '') { throw new Error('NODE_ID is required for high-availability setups'); } diff --git a/yarn-project/validator-ha-signer/tsconfig.json b/yarn-project/validator-ha-signer/tsconfig.json index ac3e90b38446..33971f5eff11 100644 --- a/yarn-project/validator-ha-signer/tsconfig.json +++ b/yarn-project/validator-ha-signer/tsconfig.json @@ -12,6 +12,9 @@ { "path": "../foundation" }, + { + "path": "../kv-store" + }, { "path": "../stdlib" }, diff --git a/yarn-project/world-state/src/synchronizer/factory.ts b/yarn-project/world-state/src/synchronizer/factory.ts index 56b18afbeaf4..f6cc55001a4a 100644 --- a/yarn-project/world-state/src/synchronizer/factory.ts +++ b/yarn-project/world-state/src/synchronizer/factory.ts @@ -1,6 +1,6 @@ import type { LoggerBindings } from '@aztec/foundation/log'; -import type { DataStoreConfig } from '@aztec/kv-store/config'; import type { L2BlockSource } from '@aztec/stdlib/block'; +import type { DataStoreConfig } from '@aztec/stdlib/kv-store'; import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging'; import type { PublicDataTreeLeaf } from '@aztec/stdlib/trees'; import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client'; diff --git a/yarn-project/world-state/src/test/integration.test.ts b/yarn-project/world-state/src/test/integration.test.ts index 4f75871da279..8ceb875d4669 100644 --- a/yarn-project/world-state/src/test/integration.test.ts +++ b/yarn-project/world-state/src/test/integration.test.ts @@ -5,8 +5,8 @@ import type { Fr } from '@aztec/foundation/curves/bn254'; import { EthAddress } from '@aztec/foundation/eth-address'; import { type Logger, createLogger } from '@aztec/foundation/log'; import { sleep } from '@aztec/foundation/sleep'; -import type { DataStoreConfig } from '@aztec/kv-store/config'; import type { Checkpoint } from '@aztec/stdlib/checkpoint'; +import type { DataStoreConfig } from '@aztec/stdlib/kv-store'; import { MerkleTreeId } from '@aztec/stdlib/trees'; import { describe, jest } from '@jest/globals'; diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index b94c9a028b37..eac9a7f96775 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -2260,6 +2260,7 @@ __metadata: dependencies: "@aztec/ethereum": "workspace:^" "@aztec/foundation": "workspace:^" + "@aztec/kv-store": "workspace:^" "@aztec/stdlib": "workspace:^" "@aztec/telemetry-client": "workspace:^" "@electric-sql/pglite": "npm:^0.3.14"