diff --git a/spartan/environments/devnet.env b/spartan/environments/devnet.env index 6fef140f7011..07479bd312b9 100644 --- a/spartan/environments/devnet.env +++ b/spartan/environments/devnet.env @@ -74,4 +74,4 @@ RPC_INGRESS_HOSTS="[\"$NAMESPACE.aztec-labs.com\"]" RPC_INGRESS_STATIC_IP_NAME=$NAMESPACE-rpc-ip RPC_INGRESS_SSL_CERT_NAMES="[\"$NAMESPACE-rpc-cert\"]" -WS_NUM_HISTORIC_BLOCKS=300 +WS_NUM_HISTORIC_CHECKPOINTS=300 diff --git a/spartan/scripts/deploy_network.sh b/spartan/scripts/deploy_network.sh index 9d8103c0794b..d0e4e189ba28 100755 --- a/spartan/scripts/deploy_network.sh +++ b/spartan/scripts/deploy_network.sh @@ -596,7 +596,7 @@ FULL_NODE_INCLUDE_METRICS = "${FULL_NODE_INCLUDE_METRICS-null}" LOG_LEVEL = "${LOG_LEVEL}" FISHERMAN_LOG_LEVEL = "${FISHERMAN_LOG_LEVEL}" -WS_NUM_HISTORIC_BLOCKS = ${WS_NUM_HISTORIC_BLOCKS:-null} +WS_NUM_HISTORIC_CHECKPOINTS = ${WS_NUM_HISTORIC_CHECKPOINTS:-null} P2P_PUBLIC_IP = ${P2P_PUBLIC_IP} P2P_NODEPORT_ENABLED = ${P2P_NODEPORT_ENABLED} diff --git a/spartan/terraform/deploy-aztec-infra/main.tf b/spartan/terraform/deploy-aztec-infra/main.tf index 347939595c5b..85c891329229 100644 --- a/spartan/terraform/deploy-aztec-infra/main.tf +++ b/spartan/terraform/deploy-aztec-infra/main.tf @@ -218,7 +218,7 @@ locals { "validator.node.env.P2P_GOSSIPSUB_DHI" = var.P2P_GOSSIPSUB_DHI "validator.node.env.P2P_DROP_TX" = var.P2P_DROP_TX "validator.node.env.P2P_DROP_TX_CHANCE" = var.P2P_DROP_TX_CHANCE - "validator.node.env.WS_NUM_HISTORIC_BLOCKS" = var.WS_NUM_HISTORIC_BLOCKS + "validator.node.env.WS_NUM_HISTORIC_CHECKPOINTS" = var.WS_NUM_HISTORIC_CHECKPOINTS "validator.node.env.TX_COLLECTION_FILE_STORE_URLS" = var.TX_COLLECTION_FILE_STORE_URLS "validator.node.env.SEQ_SKIP_CHECKPOINT_PUBLISH_PERCENT" = var.SEQ_SKIP_CHECKPOINT_PUBLISH_PERCENT } @@ -358,7 +358,7 @@ locals { "node.node.env.P2P_GOSSIPSUB_DHI" = var.P2P_GOSSIPSUB_DHI "node.node.env.P2P_DROP_TX" = var.P2P_DROP_TX "node.node.env.P2P_DROP_TX_CHANCE" = var.P2P_DROP_TX_CHANCE - "node.node.env.WS_NUM_HISTORIC_BLOCKS" = var.WS_NUM_HISTORIC_BLOCKS + "node.node.env.WS_NUM_HISTORIC_CHECKPOINTS" = var.WS_NUM_HISTORIC_CHECKPOINTS "node.node.env.TX_COLLECTION_FILE_STORE_URLS" = var.TX_COLLECTION_FILE_STORE_URLS "node.service.p2p.nodePortEnabled" = var.P2P_NODEPORT_ENABLED "node.service.p2p.announcePort" = local.p2p_port_prover @@ -438,7 +438,7 @@ locals { "node.env.P2P_GOSSIPSUB_DHI" = var.P2P_GOSSIPSUB_DHI "node.env.P2P_DROP_TX" = var.P2P_DROP_TX "node.env.P2P_DROP_TX_CHANCE" = var.P2P_DROP_TX_CHANCE - "node.env.WS_NUM_HISTORIC_BLOCKS" = var.WS_NUM_HISTORIC_BLOCKS + "node.env.WS_NUM_HISTORIC_CHECKPOINTS" = var.WS_NUM_HISTORIC_CHECKPOINTS "node.env.TX_FILE_STORE_ENABLED" = var.TX_FILE_STORE_ENABLED "node.env.TX_FILE_STORE_URL" = var.TX_FILE_STORE_URL "node.env.TX_COLLECTION_FILE_STORE_URLS" = var.TX_COLLECTION_FILE_STORE_URLS @@ -495,7 +495,7 @@ locals { "node.env.P2P_GOSSIPSUB_DHI" = var.P2P_GOSSIPSUB_DHI "node.env.P2P_DROP_TX" = var.P2P_DROP_TX "node.env.P2P_DROP_TX_CHANCE" = var.P2P_DROP_TX_CHANCE - "node.env.WS_NUM_HISTORIC_BLOCKS" = var.WS_NUM_HISTORIC_BLOCKS + "node.env.WS_NUM_HISTORIC_CHECKPOINTS" = var.WS_NUM_HISTORIC_CHECKPOINTS "node.env.TX_COLLECTION_FILE_STORE_URLS" = var.TX_COLLECTION_FILE_STORE_URLS } boot_node_host_path = "node.env.BOOT_NODE_HOST" @@ -535,7 +535,7 @@ locals { "node.env.P2P_GOSSIPSUB_DHI" = var.P2P_GOSSIPSUB_DHI "node.env.P2P_DROP_TX" = var.P2P_DROP_TX "node.env.P2P_DROP_TX_CHANCE" = var.P2P_DROP_TX_CHANCE - "node.env.WS_NUM_HISTORIC_BLOCKS" = var.WS_NUM_HISTORIC_BLOCKS + "node.env.WS_NUM_HISTORIC_CHECKPOINTS" = var.WS_NUM_HISTORIC_CHECKPOINTS "node.env.TX_COLLECTION_FILE_STORE_URLS" = var.TX_COLLECTION_FILE_STORE_URLS } boot_node_host_path = "node.env.BOOT_NODE_HOST" @@ -572,7 +572,7 @@ locals { "node.env.P2P_GOSSIPSUB_DHI" = var.P2P_GOSSIPSUB_DHI "node.env.P2P_DROP_TX" = var.P2P_DROP_TX "node.env.P2P_DROP_TX_CHANCE" = var.P2P_DROP_TX_CHANCE - "node.env.WS_NUM_HISTORIC_BLOCKS" = var.WS_NUM_HISTORIC_BLOCKS + "node.env.WS_NUM_HISTORIC_CHECKPOINTS" = var.WS_NUM_HISTORIC_CHECKPOINTS } boot_node_host_path = "node.env.BOOT_NODE_HOST" bootstrap_nodes_path = "node.env.BOOTSTRAP_NODES" diff --git a/spartan/terraform/deploy-aztec-infra/variables.tf b/spartan/terraform/deploy-aztec-infra/variables.tf index 9947d2379f43..358e2b0ec335 100644 --- a/spartan/terraform/deploy-aztec-infra/variables.tf +++ b/spartan/terraform/deploy-aztec-infra/variables.tf @@ -772,8 +772,8 @@ variable "P2P_DROP_TX_CHANCE" { default = 0 } -variable "WS_NUM_HISTORIC_BLOCKS" { - description = "Number of historic blocks for world state" +variable "WS_NUM_HISTORIC_CHECKPOINTS" { + description = "Number of historic checkpoints for world state" type = string nullable = true default = null diff --git a/yarn-project/aztec/src/cli/aztec_start_options.ts b/yarn-project/aztec/src/cli/aztec_start_options.ts index d99dfab99690..d6df6e872530 100644 --- a/yarn-project/aztec/src/cli/aztec_start_options.ts +++ b/yarn-project/aztec/src/cli/aztec_start_options.ts @@ -170,7 +170,7 @@ export const aztecStartOptions: { [key: string]: AztecStartOption[] } = { 'WORLD STATE': [ configToFlag('--world-state-data-directory', worldStateConfigMappings.worldStateDataDirectory), configToFlag('--world-state-db-map-size-kb', worldStateConfigMappings.worldStateDbMapSizeKb), - configToFlag('--world-state-block-history', worldStateConfigMappings.worldStateBlockHistory), + configToFlag('--world-state-checkpoint-history', worldStateConfigMappings.worldStateCheckpointHistory), ], // We can't easily auto-generate node options as they're parts of modules defined below 'AZTEC NODE': [ diff --git a/yarn-project/end-to-end/src/e2e_epochs/epochs_multiple.test.ts b/yarn-project/end-to-end/src/e2e_epochs/epochs_multiple.test.ts index d0f94c81e433..6703786b0f88 100644 --- a/yarn-project/end-to-end/src/e2e_epochs/epochs_multiple.test.ts +++ b/yarn-project/end-to-end/src/e2e_epochs/epochs_multiple.test.ts @@ -5,7 +5,7 @@ import { BlockNumber } from '@aztec/foundation/branded-types'; import { jest } from '@jest/globals'; import type { EndToEndContext } from '../fixtures/utils.js'; -import { EpochsTestContext, WORLD_STATE_BLOCK_HISTORY } from './epochs_test.js'; +import { EpochsTestContext, WORLD_STATE_CHECKPOINT_HISTORY } from './epochs_test.js'; jest.setTimeout(1000 * 60 * 15); @@ -50,9 +50,10 @@ describe('e2e_epochs/epochs_multiple', () => { // Check that finalized blocks are purged from world state // Right now finalization means a checkpoint is two L2 epochs deep. If this rule changes then this test needs to be updated. + // This test is setup as 1 block per checkpoint const provenBlockNumber = epochEndBlockNumber; const finalizedBlockNumber = Math.max(provenBlockNumber - context.config.aztecEpochDuration * 2, 0); - const expectedOldestHistoricBlock = Math.max(finalizedBlockNumber - WORLD_STATE_BLOCK_HISTORY + 1, 1); + const expectedOldestHistoricBlock = Math.max(finalizedBlockNumber - WORLD_STATE_CHECKPOINT_HISTORY + 1, 1); const expectedBlockRemoved = expectedOldestHistoricBlock - 1; await test.waitForNodeToSync(BlockNumber(expectedOldestHistoricBlock), 'historic'); await test.verifyHistoricBlock(BlockNumber(expectedOldestHistoricBlock), true); diff --git a/yarn-project/end-to-end/src/e2e_epochs/epochs_test.ts b/yarn-project/end-to-end/src/e2e_epochs/epochs_test.ts index a2e4eeaa2f7f..a272fc521d94 100644 --- a/yarn-project/end-to-end/src/e2e_epochs/epochs_test.ts +++ b/yarn-project/end-to-end/src/e2e_epochs/epochs_test.ts @@ -41,7 +41,7 @@ import { setup, } from '../fixtures/utils.js'; -export const WORLD_STATE_BLOCK_HISTORY = 2; +export const WORLD_STATE_CHECKPOINT_HISTORY = 2; export const WORLD_STATE_BLOCK_CHECK_INTERVAL = 50; export const ARCHIVER_POLL_INTERVAL = 50; export const DEFAULT_L1_BLOCK_TIME = process.env.CI ? 12 : 8; @@ -142,7 +142,7 @@ export class EpochsTestContext { // using the prover's eth address if the proverId is used for something in the rollup contract // Use numeric EthAddress for deterministic prover id proverId: EthAddress.fromNumber(1), - worldStateBlockHistory: WORLD_STATE_BLOCK_HISTORY, + worldStateCheckpointHistory: WORLD_STATE_CHECKPOINT_HISTORY, exitDelaySeconds: DefaultL1ContractsConfig.exitDelaySeconds, slasherFlavor: 'none', l1PublishingTime, diff --git a/yarn-project/end-to-end/src/e2e_l1_publisher/e2e_l1_publisher.test.ts b/yarn-project/end-to-end/src/e2e_l1_publisher/e2e_l1_publisher.test.ts index 9b36f58493ff..615d869d5e7c 100644 --- a/yarn-project/end-to-end/src/e2e_l1_publisher/e2e_l1_publisher.test.ts +++ b/yarn-project/end-to-end/src/e2e_l1_publisher/e2e_l1_publisher.test.ts @@ -249,7 +249,7 @@ describe('L1Publisher integration', () => { const worldStateConfig: WorldStateConfig = { worldStateBlockCheckIntervalMS: 10000, worldStateDbMapSizeKb: 10 * 1024 * 1024, - worldStateBlockHistory: 0, + worldStateCheckpointHistory: 0, }; worldStateSynchronizer = new ServerWorldStateSynchronizer(builderDb, blockSource, worldStateConfig); await worldStateSynchronizer.start(); diff --git a/yarn-project/end-to-end/src/e2e_pruned_blocks.test.ts b/yarn-project/end-to-end/src/e2e_pruned_blocks.test.ts index 65f1bd64dfad..50a8db649dd6 100644 --- a/yarn-project/end-to-end/src/e2e_pruned_blocks.test.ts +++ b/yarn-project/end-to-end/src/e2e_pruned_blocks.test.ts @@ -31,7 +31,7 @@ describe('e2e_pruned_blocks', () => { const MINT_AMOUNT = 1000n; // Don't make this value too high since we need to mine this number of empty blocks, which is relatively slow. - const WORLD_STATE_BLOCK_HISTORY = 2; + const WORLD_STATE_CHECKPOINT_HISTORY = 2; const EPOCH_LENGTH = 2; const WORLD_STATE_CHECK_INTERVAL_MS = 300; const ARCHIVER_POLLING_INTERVAL_MS = 300; @@ -47,7 +47,7 @@ describe('e2e_pruned_blocks', () => { accounts: [admin, sender, recipient], } = await setup(3, { aztecEpochDuration: EPOCH_LENGTH, - worldStateBlockHistory: WORLD_STATE_BLOCK_HISTORY, + worldStateCheckpointHistory: WORLD_STATE_CHECKPOINT_HISTORY, worldStateBlockCheckIntervalMS: WORLD_STATE_CHECK_INTERVAL_MS, archiverPollingIntervalMS: ARCHIVER_POLLING_INTERVAL_MS, aztecProofSubmissionEpochs: 1024, // effectively do not reorg @@ -93,8 +93,9 @@ describe('e2e_pruned_blocks', () => { // We now mine dummy blocks, mark them as proven and wait for the node to process them, which should result in older // blocks (notably the one with the minted note) being pruned. Given world state prunes based on the finalized tip, // and we are defining the finalized tip as two epochs behind the proven one, we need to mine two extra epochs. + // This test assumes 1 block per checkpoint await aztecNodeAdmin!.setConfig({ minTxsPerBlock: 0 }); - await waitBlocks(WORLD_STATE_BLOCK_HISTORY + EPOCH_LENGTH * 2 + 1); + await waitBlocks(WORLD_STATE_CHECKPOINT_HISTORY + EPOCH_LENGTH * 2 + 1); await cheatCodes.rollup.markAsProven(); // The same historical query we performed before should now fail since this block is not available anymore. We poll diff --git a/yarn-project/foundation/src/config/env_var.ts b/yarn-project/foundation/src/config/env_var.ts index 41a34959a210..33e4f7b66832 100644 --- a/yarn-project/foundation/src/config/env_var.ts +++ b/yarn-project/foundation/src/config/env_var.ts @@ -281,6 +281,7 @@ export type EnvVar = | 'WS_BLOCK_REQUEST_BATCH_SIZE' | 'L1_READER_VIEM_POLLING_INTERVAL_MS' | 'WS_DATA_DIRECTORY' + | 'WS_NUM_HISTORIC_CHECKPOINTS' | 'WS_NUM_HISTORIC_BLOCKS' | 'ETHEREUM_SLOT_DURATION' | 'AZTEC_SLOT_DURATION' diff --git a/yarn-project/validator-client/src/validator.integration.test.ts b/yarn-project/validator-client/src/validator.integration.test.ts index 551928645950..539c0df9b233 100644 --- a/yarn-project/validator-client/src/validator.integration.test.ts +++ b/yarn-project/validator-client/src/validator.integration.test.ts @@ -114,7 +114,7 @@ describe('ValidatorClient Integration', () => { worldStateBlockCheckIntervalMS: 20, worldStateBlockRequestBatchSize: 10, worldStateDbMapSizeKb: 1024 * 1024, - worldStateBlockHistory: 0, + worldStateCheckpointHistory: 0, }; const worldStateDb = await NativeWorldStateService.tmp(rollupAddress, true, prefilledPublicData); const synchronizer = new ServerWorldStateSynchronizer(worldStateDb, archiver, wsConfig); diff --git a/yarn-project/world-state/src/synchronizer/config.ts b/yarn-project/world-state/src/synchronizer/config.ts index 2ba5dd6b8a9e..b1f4a7321781 100644 --- a/yarn-project/world-state/src/synchronizer/config.ts +++ b/yarn-project/world-state/src/synchronizer/config.ts @@ -29,8 +29,8 @@ export interface WorldStateConfig { /** Optional directory for the world state DB, if unspecified will default to the general data directory */ worldStateDataDirectory?: string; - /** The number of historic blocks to maintain */ - worldStateBlockHistory: number; + /** The number of historic checkpoints worth of blocks to maintain */ + worldStateCheckpointHistory: number; } export const worldStateConfigMappings: ConfigMappingsType = { @@ -84,9 +84,11 @@ export const worldStateConfigMappings: ConfigMappingsType = { env: 'WS_DATA_DIRECTORY', description: 'Optional directory for the world state database', }, - worldStateBlockHistory: { - env: 'WS_NUM_HISTORIC_BLOCKS', - description: 'The number of historic blocks to maintain. Values less than 1 mean all history is maintained', + worldStateCheckpointHistory: { + env: 'WS_NUM_HISTORIC_CHECKPOINTS', + description: + 'The number of historic checkpoints worth of blocks to maintain. Values less than 1 mean all history is maintained', + fallback: ['WS_NUM_HISTORIC_BLOCKS'], ...numberConfigHelper(64), }, }; diff --git a/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.test.ts b/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.test.ts index bc5576d98d95..c9eebe16914a 100644 --- a/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.test.ts +++ b/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.test.ts @@ -80,7 +80,7 @@ describe('ServerWorldStateSynchronizer', () => { const config: WorldStateConfig = { worldStateBlockCheckIntervalMS: 100, worldStateDbMapSizeKb: 1024 * 1024, - worldStateBlockHistory: 0, + worldStateCheckpointHistory: 0, }; server = new TestWorldStateSynchronizer(merkleTreeDb, blockAndMessagesSource, config, l2BlockStream); diff --git a/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.ts b/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.ts index daa67b1882d1..38a4e9ce81e5 100644 --- a/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.ts +++ b/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.ts @@ -1,5 +1,5 @@ import { GENESIS_BLOCK_HEADER_HASH, INITIAL_L2_BLOCK_NUM, INITIAL_L2_CHECKPOINT_NUM } from '@aztec/constants'; -import { BlockNumber } from '@aztec/foundation/branded-types'; +import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types'; import type { Fr } from '@aztec/foundation/curves/bn254'; import { type Logger, createLogger } from '@aztec/foundation/log'; import { promiseWithResolvers } from '@aztec/foundation/promise'; @@ -64,7 +64,7 @@ export class ServerWorldStateSynchronizer private readonly log: Logger = createLogger('world_state'), ) { this.merkleTreeCommitted = this.merkleTreeDb.getCommitted(); - this.historyToKeep = config.worldStateBlockHistory < 1 ? undefined : config.worldStateBlockHistory; + this.historyToKeep = config.worldStateCheckpointHistory < 1 ? undefined : config.worldStateCheckpointHistory; this.log.info( `Created world state synchroniser with block history of ${ this.historyToKeep === undefined ? 'infinity' : this.historyToKeep @@ -364,12 +364,37 @@ export class ServerWorldStateSynchronizer if (this.historyToKeep === undefined) { return; } - const newHistoricBlock = summary.finalizedBlockNumber - this.historyToKeep + 1; - if (newHistoricBlock <= 1) { + // Get the checkpointed block for the finalized block number + const finalisedCheckpoint = await this.l2BlockSource.getCheckpointedBlock(summary.finalizedBlockNumber); + if (finalisedCheckpoint === undefined) { + this.log.warn( + `Failed to retrieve checkpointed block for finalized block number: ${summary.finalizedBlockNumber}`, + ); + return; + } + // Compute the required historic checkpoint number + const newHistoricCheckpointNumber = finalisedCheckpoint.checkpointNumber - this.historyToKeep + 1; + if (newHistoricCheckpointNumber <= 1) { + return; + } + // Retrieve the historic checkpoint + const historicCheckpoints = await this.l2BlockSource.getCheckpoints( + CheckpointNumber(newHistoricCheckpointNumber), + 1, + ); + if (historicCheckpoints.length === 0 || historicCheckpoints[0] === undefined) { + this.log.warn(`Failed to retrieve checkpoint number ${newHistoricCheckpointNumber} from Archiver`); + return; + } + const historicCheckpoint = historicCheckpoints[0]; + if (historicCheckpoint.checkpoint.blocks.length === 0 || historicCheckpoint.checkpoint.blocks[0] === undefined) { + this.log.warn(`Retrieved checkpoint number ${newHistoricCheckpointNumber} has no blocks!`); return; } - this.log.verbose(`Pruning historic blocks to ${newHistoricBlock}`); - const status = await this.merkleTreeDb.removeHistoricalBlocks(BlockNumber(newHistoricBlock)); + // Find the block at the start of the checkpoint and remove blocks up to this one + const newHistoricBlock = historicCheckpoint.checkpoint.blocks[0]; + this.log.verbose(`Pruning historic blocks to ${newHistoricBlock.number}`); + const status = await this.merkleTreeDb.removeHistoricalBlocks(BlockNumber(newHistoricBlock.number)); this.log.debug(`World state summary `, status.summary); } diff --git a/yarn-project/world-state/src/test/integration.test.ts b/yarn-project/world-state/src/test/integration.test.ts index 9a784bcfec27..4f75871da279 100644 --- a/yarn-project/world-state/src/test/integration.test.ts +++ b/yarn-project/world-state/src/test/integration.test.ts @@ -52,7 +52,7 @@ describe('world-state integration', () => { worldStateBlockCheckIntervalMS: 20, worldStateBlockRequestBatchSize: 5, worldStateDbMapSizeKb: 1024 * 1024, - worldStateBlockHistory: 0, + worldStateCheckpointHistory: 0, }; archiver = new MockPrefilledArchiver(checkpoints);