From b5de600ce85eb905f11ef02c915457331e58fb6d Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Tue, 9 Jun 2026 11:54:32 -0300 Subject: [PATCH 1/8] fix(p2p): resolve checkpoint tips from stored ids and fail loudly on corruption MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No L2Tips provider may silently report checkpoint zero for a cursor that points at a real (non-genesis) block. getCheckpointId now throws for all four checkpoint-bearing cursors (checkpointed, proposedCheckpoint, proven, finalized) when a real block has no resolvable checkpoint, but only on genuine store corruption — never on a legitimate skipped-history prune. - Carry a checkpoint id in chain-proven and chain-finalized events (populated from sourceTips), so a cursor that legitimately leads the locally-checkpointed frontier can still resolve its checkpoint. - Store a checkpoint id per cursor (new l2_tip_checkpoints map in the KV store; a Map in the memory store). It is written wherever a checkpoint-bearing cursor advances: chain-checkpointed (checkpointed and, when advanced, proposedCheckpoint), chain-proven, chain-finalized. - In handleChainPruned, for each cursor clamped backward, resolve a consistent id from the local block->checkpoint mapping; when the prune target is a synced-but-unmapped block (skipped history) clamp that cursor to genesis instead, so it re-syncs rather than sitting on an id-less block. The prune handler never throws. - getCheckpointId order: genesis -> stored per-cursor id -> block->checkpoint mapping (back-compat for stores written before per-cursor ids) -> throw. Because the writers never leave a cursor on an id-less real block, the throw is reachable only on genuine corruption. The pxe l2_tip_checkpoints sub-store is read-defaultable: existing on-disk DBs start with it empty, getTipCheckpoint returns undefined, and resolution falls back to the block->checkpoint mapping, so no PXE_DATA_SCHEMA_VERSION bump is needed. World-state is intentionally left unchanged: its merkle trees live in a native store with no shared TS transaction, so checkpoint tracking cannot be updated atomically with merkle commits/unwinds without risking drift, and no consumer reads world-state checkpoint ids for correctness. --- .../kv-store/src/stores/l2_tips_store.ts | 19 ++- .../block_synchronizer.test.ts | 3 + .../__snapshots__/L2TipsKVStore.json | 14 ++ .../__snapshots__/opened_stores.json | 4 + .../schema_tests.ts | 4 + .../src/block/l2_block_stream/interfaces.ts | 2 + .../l2_block_stream/l2_block_stream.test.ts | 32 ++--- .../block/l2_block_stream/l2_block_stream.ts | 7 +- .../l2_block_stream/l2_tips_memory_store.ts | 12 +- .../l2_block_stream/l2_tips_store_base.ts | 75 ++++++++++- .../block/test/l2_tips_store_test_suite.ts | 123 ++++++++++++++++-- .../world-state/src/test/integration.test.ts | 1 + 12 files changed, 263 insertions(+), 33 deletions(-) diff --git a/yarn-project/kv-store/src/stores/l2_tips_store.ts b/yarn-project/kv-store/src/stores/l2_tips_store.ts index 8d34baa72736..61ad899f68ab 100644 --- a/yarn-project/kv-store/src/stores/l2_tips_store.ts +++ b/yarn-project/kv-store/src/stores/l2_tips_store.ts @@ -1,16 +1,20 @@ import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types'; -import { type BlockHash, type L2BlockTag, L2TipsStoreBase } from '@aztec/stdlib/block'; +import { type BlockHash, type CheckpointId, type L2BlockTag, L2TipsStoreBase } from '@aztec/stdlib/block'; import { PublishedCheckpoint } from '@aztec/stdlib/checkpoint'; import type { AztecAsyncMap } from '../interfaces/map.js'; import type { AztecAsyncKVStore } from '../interfaces/store.js'; +/** Serialized form of a per-tip checkpoint id stored in the KV store. */ +type StoredCheckpointId = { number: number; hash: string }; + /** * Persistent implementation of L2 tips store backed by a KV store. * Used by nodes that need to persist chain state across restarts. */ export class L2TipsKVStore extends L2TipsStoreBase { private readonly l2TipsStore: AztecAsyncMap; + private readonly l2TipCheckpointsStore: AztecAsyncMap; private readonly l2BlockHashesStore: AztecAsyncMap; private readonly l2BlockNumberToCheckpointNumberStore: AztecAsyncMap; private readonly l2CheckpointStore: AztecAsyncMap; @@ -22,6 +26,7 @@ export class L2TipsKVStore extends L2TipsStoreBase { ) { super(initialBlockHash); this.l2TipsStore = store.openMap([namespace, 'l2_tips'].join('_')); + this.l2TipCheckpointsStore = store.openMap([namespace, 'l2_tip_checkpoints'].join('_')); this.l2BlockHashesStore = store.openMap([namespace, 'l2_block_hashes'].join('_')); this.l2BlockNumberToCheckpointNumberStore = store.openMap( [namespace, 'l2_block_number_to_checkpoint_number'].join('_'), @@ -37,6 +42,18 @@ export class L2TipsKVStore extends L2TipsStoreBase { return this.l2TipsStore.set(tag, blockNumber); } + protected async getTipCheckpoint(tag: L2BlockTag): Promise { + const stored = await this.l2TipCheckpointsStore.getAsync(tag); + if (stored === undefined) { + return undefined; + } + return { number: CheckpointNumber(stored.number), hash: stored.hash }; + } + + protected setTipCheckpoint(tag: L2BlockTag, checkpoint: CheckpointId): Promise { + return this.l2TipCheckpointsStore.set(tag, { number: checkpoint.number, hash: checkpoint.hash }); + } + protected getStoredBlockHash(blockNumber: BlockNumber): Promise { return this.l2BlockHashesStore.getAsync(blockNumber); } diff --git a/yarn-project/pxe/src/block_synchronizer/block_synchronizer.test.ts b/yarn-project/pxe/src/block_synchronizer/block_synchronizer.test.ts index 057659691913..aae2b495cb88 100644 --- a/yarn-project/pxe/src/block_synchronizer/block_synchronizer.test.ts +++ b/yarn-project/pxe/src/block_synchronizer/block_synchronizer.test.ts @@ -265,6 +265,7 @@ describe('BlockSynchronizer', () => { await synchronizer.handleBlockStreamEvent({ type: 'chain-finalized', block: block9, + checkpoint: makeL2CheckpointId(CheckpointNumber(1), Fr.random().toString()), }); // Finalization is a no-op for storage under delete-on-prune, every row at and below the tip survives. @@ -408,6 +409,7 @@ describe('BlockSynchronizer', () => { await synchronizer.handleBlockStreamEvent({ type: 'chain-proven', block: { number: BlockNumber(5), hash: '0x789' }, + checkpoint: { number: CheckpointNumber(1), hash: '0x789c' }, }); const obtainedHeader = await anchorBlockStore.getBlockHeader(); @@ -428,6 +430,7 @@ describe('BlockSynchronizer', () => { await synchronizer.handleBlockStreamEvent({ type: 'chain-finalized', block: { number: BlockNumber(10), hash: '0xabc' }, + checkpoint: { number: CheckpointNumber(2), hash: '0xabcc' }, }); const obtainedHeader = await anchorBlockStore.getBlockHeader(); diff --git a/yarn-project/pxe/src/storage/backwards_compatibility_tests/__snapshots__/L2TipsKVStore.json b/yarn-project/pxe/src/storage/backwards_compatibility_tests/__snapshots__/L2TipsKVStore.json index bce86863558b..dbe46075ea67 100644 --- a/yarn-project/pxe/src/storage/backwards_compatibility_tests/__snapshots__/L2TipsKVStore.json +++ b/yarn-project/pxe/src/storage/backwards_compatibility_tests/__snapshots__/L2TipsKVStore.json @@ -17,6 +17,20 @@ "value": "num:79" } ], + "pxe_l2_tip_checkpoints": [ + { + "key": "utf8:checkpointed", + "value": "{\"number\":47,\"hash\":\"0x00a21eaf943186f15f609a8a76fb0bee7ce3e78c86ef95f5466614e59546eb19\"}" + }, + { + "key": "utf8:proposedCheckpoint", + "value": "{\"number\":47,\"hash\":\"0x00a21eaf943186f15f609a8a76fb0bee7ce3e78c86ef95f5466614e59546eb19\"}" + }, + { + "key": "utf8:proven", + "value": "{\"number\":47,\"hash\":\"0x0000000000000000000000000000000000000000000000000000000000000059\"}" + } + ], "pxe_l2_block_hashes": [ { "key": "num:179", diff --git a/yarn-project/pxe/src/storage/backwards_compatibility_tests/__snapshots__/opened_stores.json b/yarn-project/pxe/src/storage/backwards_compatibility_tests/__snapshots__/opened_stores.json index 6c031f74812f..aa94a1ebd3a1 100644 --- a/yarn-project/pxe/src/storage/backwards_compatibility_tests/__snapshots__/opened_stores.json +++ b/yarn-project/pxe/src/storage/backwards_compatibility_tests/__snapshots__/opened_stores.json @@ -97,6 +97,10 @@ "name": "pxe_l2_checkpoint_store", "kind": "map" }, + { + "name": "pxe_l2_tip_checkpoints", + "kind": "map" + }, { "name": "pxe_l2_tips", "kind": "map" diff --git a/yarn-project/pxe/src/storage/backwards_compatibility_tests/schema_tests.ts b/yarn-project/pxe/src/storage/backwards_compatibility_tests/schema_tests.ts index 9a1e54a2a2cf..b56a5738d313 100644 --- a/yarn-project/pxe/src/storage/backwards_compatibility_tests/schema_tests.ts +++ b/yarn-project/pxe/src/storage/backwards_compatibility_tests/schema_tests.ts @@ -269,10 +269,14 @@ export const SCHEMA_TESTS: readonly SchemaTest[] = [ await l2TipsStore.handleBlockStreamEvent({ type: 'chain-proven', block: { number: BlockNumber(79), hash: new Fr(83n).toString() }, + checkpoint: { number: CheckpointNumber(47), hash: new Fr(89n).toString() }, }); }, snapshotStore: async kvStore => ({ pxe_l2_tips: await snapshotMap(kvStore.openMap('pxe_l2_tips')), + pxe_l2_tip_checkpoints: await snapshotMap( + kvStore.openMap('pxe_l2_tip_checkpoints'), + ), pxe_l2_block_hashes: await snapshotMap(kvStore.openMap('pxe_l2_block_hashes')), pxe_l2_block_number_to_checkpoint_number: await snapshotMap( kvStore.openMap('pxe_l2_block_number_to_checkpoint_number'), diff --git a/yarn-project/stdlib/src/block/l2_block_stream/interfaces.ts b/yarn-project/stdlib/src/block/l2_block_stream/interfaces.ts index bf5831fa592f..0f157f797256 100644 --- a/yarn-project/stdlib/src/block/l2_block_stream/interfaces.ts +++ b/yarn-project/stdlib/src/block/l2_block_stream/interfaces.ts @@ -39,10 +39,12 @@ export type L2BlockStreamEvent = | /** Reports new proven block. */ { type: 'chain-proven'; block: L2BlockId; + checkpoint: CheckpointId; } | /** Reports new finalized block (proven and finalized on L1). */ { type: 'chain-finalized'; block: L2BlockId; + checkpoint: CheckpointId; }; export type L2TipsStore = L2BlockStreamEventHandler & L2BlockStreamLocalDataProvider; diff --git a/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.test.ts b/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.test.ts index 7a949476dd88..bc9fb7b62ccf 100644 --- a/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.test.ts +++ b/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.test.ts @@ -256,8 +256,8 @@ describe('L2BlockStream', () => { await blockStream.work(); expect(handler.events).toEqual([ { type: 'blocks-added', blocks: times(5, i => makeBlock(i + 41)) }, - { type: 'chain-proven', block: makeBlockId(40) }, - { type: 'chain-finalized', block: makeBlockId(35) }, + { type: 'chain-proven', block: makeBlockId(40), checkpoint: makeCheckpointId(40) }, + { type: 'chain-finalized', block: makeBlockId(35), checkpoint: makeCheckpointId(35) }, ] satisfies L2BlockStreamEvent[]); }); @@ -384,8 +384,8 @@ describe('L2BlockStream', () => { expectBlocksAdded([30]), expectCheckpointed(30), { type: 'blocks-added', blocks: times(5, i => makeBlock(i + 31)) }, - { type: 'chain-proven', block: makeBlockId(25) }, - { type: 'chain-finalized', block: makeBlockId(10) }, + { type: 'chain-proven', block: makeBlockId(25), checkpoint: makeCheckpointId(25) }, + { type: 'chain-finalized', block: makeBlockId(10), checkpoint: makeCheckpointId(10) }, ]); handler.clearEvents(); @@ -1135,7 +1135,7 @@ describe('L2BlockStream', () => { expectBlocksAdded([7, 8, 9]), expectCheckpointed(3), expectBlocksAdded([10, 11, 12]), - { type: 'chain-proven', block: makeBlockId(6) }, + { type: 'chain-proven', block: makeBlockId(6), checkpoint: makeCheckpointId(2) }, ]); handler.clearEvents(); @@ -1191,7 +1191,7 @@ describe('L2BlockStream', () => { expectCheckpointed(4), expectBlocksAdded([13, 14, 15]), expectCheckpointed(5), - { type: 'chain-proven', block: makeBlockId(9) }, + { type: 'chain-proven', block: makeBlockId(9), checkpoint: makeCheckpointId(3) }, ]); }); @@ -1508,8 +1508,8 @@ describe('L2BlockStream', () => { expectBlocksAdded([1, 2, 3]), expectBlocksAdded([4, 5, 6]), expectBlocksAdded([7, 8, 9]), - { type: 'chain-proven', block: makeBlockId(6) }, - { type: 'chain-finalized', block: makeBlockId(3) }, + { type: 'chain-proven', block: makeBlockId(6), checkpoint: makeCheckpointId(2) }, + { type: 'chain-finalized', block: makeBlockId(3), checkpoint: makeCheckpointId(1) }, ]); }); @@ -1541,8 +1541,8 @@ describe('L2BlockStream', () => { expectBlocksAdded([6]), expectBlocksAdded([7, 8, 9]), expectBlocksAdded([10, 11, 12]), - { type: 'chain-proven', block: makeBlockId(9) }, - { type: 'chain-finalized', block: makeBlockId(6) }, + { type: 'chain-proven', block: makeBlockId(9), checkpoint: makeCheckpointId(3) }, + { type: 'chain-finalized', block: makeBlockId(6), checkpoint: makeCheckpointId(2) }, ]); }); @@ -1629,8 +1629,8 @@ describe('L2BlockStream', () => { // Instead of fetching the next local block (6), we skip ahead to the latest finalized (35) and go from there. expect(handler.events).toEqual([ { type: 'blocks-added', blocks: times(6, i => makeBlock(i + 35)) }, - { type: 'chain-proven', block: makeBlockId(38) }, - { type: 'chain-finalized', block: makeBlockId(35) }, + { type: 'chain-proven', block: makeBlockId(38), checkpoint: makeCheckpointId(38) }, + { type: 'chain-finalized', block: makeBlockId(35), checkpoint: makeCheckpointId(35) }, ] satisfies L2BlockStreamEvent[]); }); @@ -1689,8 +1689,8 @@ describe('L2BlockStream', () => { expectBlocksAdded([9]), expectCheckpointed(9), { type: 'blocks-added', blocks: times(3, i => makeBlock(i + 10)) }, - { type: 'chain-proven', block: makeBlockId(9) }, - { type: 'chain-finalized', block: makeBlockId(6) }, + { type: 'chain-proven', block: makeBlockId(9), checkpoint: makeCheckpointId(9) }, + { type: 'chain-finalized', block: makeBlockId(6), checkpoint: makeCheckpointId(6) }, ]); }); @@ -1760,8 +1760,8 @@ describe('L2BlockStream', () => { }), }), { type: 'blocks-added', blocks: times(2, i => makeBlock(i + 39)) }, - { type: 'chain-proven', block: makeBlockId(38) }, - { type: 'chain-finalized', block: makeBlockId(35) }, + { type: 'chain-proven', block: makeBlockId(38), checkpoint: makeCheckpointId(38) }, + { type: 'chain-finalized', block: makeBlockId(35), checkpoint: makeCheckpointId(35) }, ]); }); }); diff --git a/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.ts b/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.ts index 9a38e28d8d9c..122f4aa55198 100644 --- a/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.ts +++ b/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.ts @@ -261,10 +261,15 @@ export class L2BlockStream { await this.emitEvent({ type: 'chain-proven', block: sourceTips.proven.block, + checkpoint: sourceTips.proven.checkpoint, }); } if (localTips.finalized !== undefined && sourceTips.finalized.block.number !== localTips.finalized.block.number) { - await this.emitEvent({ type: 'chain-finalized', block: sourceTips.finalized.block }); + await this.emitEvent({ + type: 'chain-finalized', + block: sourceTips.finalized.block, + checkpoint: sourceTips.finalized.checkpoint, + }); } } catch (err: any) { if (err.name === 'AbortError') { diff --git a/yarn-project/stdlib/src/block/l2_block_stream/l2_tips_memory_store.ts b/yarn-project/stdlib/src/block/l2_block_stream/l2_tips_memory_store.ts index ab5f9e5f5752..8b7c3c205252 100644 --- a/yarn-project/stdlib/src/block/l2_block_stream/l2_tips_memory_store.ts +++ b/yarn-project/stdlib/src/block/l2_block_stream/l2_tips_memory_store.ts @@ -2,7 +2,7 @@ import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types'; import type { PublishedCheckpoint } from '../../checkpoint/published_checkpoint.js'; import type { BlockHash } from '../block_hash.js'; -import type { L2BlockTag } from '../l2_block_source.js'; +import type { CheckpointId, L2BlockTag } from '../l2_block_source.js'; import { L2TipsStoreBase } from './l2_tips_store_base.js'; /** @@ -15,6 +15,7 @@ export class L2TipsMemoryStore extends L2TipsStoreBase { } private readonly tips = new Map(); + private readonly tipCheckpoints = new Map(); private readonly blockHashes = new Map(); private readonly blockToCheckpoint = new Map(); private readonly checkpoints = new Map(); @@ -28,6 +29,15 @@ export class L2TipsMemoryStore extends L2TipsStoreBase { return Promise.resolve(); } + protected getTipCheckpoint(tag: L2BlockTag): Promise { + return Promise.resolve(this.tipCheckpoints.get(tag)); + } + + protected setTipCheckpoint(tag: L2BlockTag, checkpoint: CheckpointId): Promise { + this.tipCheckpoints.set(tag, checkpoint); + return Promise.resolve(); + } + protected getStoredBlockHash(blockNumber: BlockNumber): Promise { return Promise.resolve(this.blockHashes.get(blockNumber)); } diff --git a/yarn-project/stdlib/src/block/l2_block_stream/l2_tips_store_base.ts b/yarn-project/stdlib/src/block/l2_block_stream/l2_tips_store_base.ts index ca5c10b5c30d..7b0589db2cfc 100644 --- a/yarn-project/stdlib/src/block/l2_block_stream/l2_tips_store_base.ts +++ b/yarn-project/stdlib/src/block/l2_block_stream/l2_tips_store_base.ts @@ -26,6 +26,12 @@ export abstract class L2TipsStoreBase implements L2BlockStreamEventHandler, L2Bl /** Sets the block number for a given tag. */ protected abstract setTip(tag: L2BlockTag, blockNumber: BlockNumber): Promise; + /** Gets the checkpoint id recorded for a given tag, if any. */ + protected abstract getTipCheckpoint(tag: L2BlockTag): Promise; + + /** Records the checkpoint id for a given tag. */ + protected abstract setTipCheckpoint(tag: L2BlockTag, checkpoint: CheckpointId): Promise; + /** Gets the block hash for a given block number. */ protected abstract getStoredBlockHash(blockNumber: BlockNumber): Promise; @@ -139,15 +145,54 @@ export abstract class L2TipsStoreBase implements L2BlockStreamEventHandler, L2Bl private async getCheckpointId(tag: L2BlockTag): Promise { const blockNumber = await this.getTip(tag); if (blockNumber === undefined || blockNumber === 0) { - return { number: CheckpointNumber.ZERO, hash: GENESIS_CHECKPOINT_HEADER_HASH.toString() }; + return this.genesisCheckpointId(); + } + // Prefer the checkpoint id recorded for this cursor when it was last advanced. This lets a cursor + // resolve correctly even when it legitimately leads the locally-checkpointed frontier (skipped + // history / startingBlock), where no local block->checkpoint mapping exists. + const storedCheckpoint = await this.getTipCheckpoint(tag); + if (storedCheckpoint !== undefined) { + return storedCheckpoint; + } + // Fall back to the block->checkpoint mapping for stores written before per-cursor ids existed. + const checkpointNumber = await this.getCheckpointNumberForBlock(blockNumber); + if (checkpointNumber !== undefined) { + const checkpoint = await this.getCheckpoint(checkpointNumber); + if (!checkpoint) { + throw new Error(`Checkpoint not found for checkpoint number ${checkpointNumber}`); + } + return { number: checkpointNumber, hash: checkpoint.checkpoint.hash().toString() }; + } + // A cursor on a real (non-genesis) block with neither a stored id nor a mapping is genuine store + // corruption. The writers (handleChainCheckpointed/Proven/Finalized/Pruned) always record an id or + // clamp to genesis, so reaching here means the store was corrupted. Fail loudly rather than silently + // reporting checkpoint zero, which would drive a checkpoint-replay storm. + throw new Error( + `No checkpoint id recorded for ${tag} tip at block ${blockNumber} and no block->checkpoint mapping found; ` + + `the L2 tips store is corrupted`, + ); + } + + private genesisCheckpointId(): CheckpointId { + return { number: CheckpointNumber.ZERO, hash: GENESIS_CHECKPOINT_HEADER_HASH.toString() }; + } + + /** + * Resolves the checkpoint id for a block from the local block->checkpoint mapping, or genesis for block + * 0. Returns undefined when the block is a real block with no local mapping (used by the prune handler + * to decide whether a clamped cursor can keep the prune target or must fall back to genesis). + */ + private async resolveCheckpointIdForBlock(blockNumber: BlockNumber): Promise { + if (blockNumber === 0) { + return this.genesisCheckpointId(); } const checkpointNumber = await this.getCheckpointNumberForBlock(blockNumber); if (checkpointNumber === undefined) { - return { number: CheckpointNumber.ZERO, hash: GENESIS_CHECKPOINT_HEADER_HASH.toString() }; + return undefined; } const checkpoint = await this.getCheckpoint(checkpointNumber); if (!checkpoint) { - throw new Error(`Checkpoint not found for checkpoint number ${checkpointNumber}`); + return undefined; } return { number: checkpointNumber, hash: checkpoint.checkpoint.hash().toString() }; } @@ -170,13 +215,19 @@ export abstract class L2TipsStoreBase implements L2BlockStreamEventHandler, L2Bl return; } await this.runInTransaction(async () => { + const checkpointId: CheckpointId = { + number: event.checkpoint.checkpoint.number, + hash: event.checkpoint.checkpoint.hash().toString(), + }; await this.saveTag('checkpointed', event.block); + await this.setTipCheckpoint('checkpointed', checkpointId); await this.saveCheckpoint(event.checkpoint); // proposedCheckpoint is always >= checkpointed. If checkpointed has caught up // or surpassed it, advance proposedCheckpoint to match. const proposedCheckpointBlock = await this.getBlockId('proposedCheckpoint'); if (event.block.number > proposedCheckpointBlock.number) { await this.saveTag('proposedCheckpoint', event.block); + await this.setTipCheckpoint('proposedCheckpoint', checkpointId); } }); } @@ -191,10 +242,24 @@ export abstract class L2TipsStoreBase implements L2BlockStreamEventHandler, L2Bl // uncheckpointed block leaves them on a block with no checkpoint mapping, which getCheckpointId // would otherwise resolve to checkpoint zero and drive a replay storm. await this.saveTag('proposed', event.block); + + // For each checkpoint-bearing cursor clamped to the prune target, resolve a consistent checkpoint + // id so the cursor never sits on a real block with no resolvable id (which getCheckpointId would + // otherwise throw on). When the prune target is a confirmed boundary the node has mapped, derive + // its id from the local mapping; otherwise the prune target is a synced-but-unmapped block (the + // skipped-history case), so clamp the cursor to genesis instead — this re-syncs cleanly rather + // than bricking the read. We never throw here. + const targetCheckpointId = await this.resolveCheckpointIdForBlock(event.block.number); for (const tag of ['checkpointed', 'proposedCheckpoint', 'proven'] as const) { const current = await this.getTip(tag); if (current !== undefined && current > event.block.number) { - await this.saveTag(tag, event.block); + if (targetCheckpointId !== undefined) { + await this.saveTag(tag, event.block); + await this.setTipCheckpoint(tag, targetCheckpointId); + } else { + await this.setTip(tag, BlockNumber.ZERO); + await this.setTipCheckpoint(tag, this.genesisCheckpointId()); + } } } }); @@ -206,6 +271,7 @@ export abstract class L2TipsStoreBase implements L2BlockStreamEventHandler, L2Bl } await this.runInTransaction(async () => { await this.saveTag('proven', event.block); + await this.setTipCheckpoint('proven', event.checkpoint); }); } @@ -215,6 +281,7 @@ export abstract class L2TipsStoreBase implements L2BlockStreamEventHandler, L2Bl } await this.runInTransaction(async () => { await this.saveTag('finalized', event.block); + await this.setTipCheckpoint('finalized', event.checkpoint); const finalizedCheckpointNumber = await this.getCheckpointNumberForBlock(event.block.number); // Cap the deletion bound at the lowest live tip. This should always be the finalized tip, but diff --git a/yarn-project/stdlib/src/block/test/l2_tips_store_test_suite.ts b/yarn-project/stdlib/src/block/test/l2_tips_store_test_suite.ts index 12af115cf97a..89b9990e9471 100644 --- a/yarn-project/stdlib/src/block/test/l2_tips_store_test_suite.ts +++ b/yarn-project/stdlib/src/block/test/l2_tips_store_test_suite.ts @@ -6,6 +6,7 @@ import { GENESIS_CHECKPOINT_HEADER_HASH, L2Block, type L2BlockId, + type L2BlockTag, type L2TipId, } from '@aztec/stdlib/block'; import { Checkpoint, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint'; @@ -153,7 +154,11 @@ export function testL2TipsStore(makeTipsStore: () => Promise) { await tipsStore.handleBlockStreamEvent(await makeCheckpointedEvent(checkpoint1)); // Prove up to block 5 - await tipsStore.handleBlockStreamEvent({ type: 'chain-proven', block: makeBlockId(5) }); + await tipsStore.handleBlockStreamEvent({ + type: 'chain-proven', + block: makeBlockId(5), + checkpoint: makeCheckpointIdForBlock(5), + }); const tips = await tipsStore.getL2Tips(); expect(tips.proposed).toEqual(makeTip(5)); @@ -173,8 +178,16 @@ export function testL2TipsStore(makeTipsStore: () => Promise) { await tipsStore.handleBlockStreamEvent(await makeCheckpointedEvent(checkpoint1)); // Prove and finalize - await tipsStore.handleBlockStreamEvent({ type: 'chain-proven', block: makeBlockId(5) }); - await tipsStore.handleBlockStreamEvent({ type: 'chain-finalized', block: makeBlockId(5) }); + await tipsStore.handleBlockStreamEvent({ + type: 'chain-proven', + block: makeBlockId(5), + checkpoint: makeCheckpointIdForBlock(5), + }); + await tipsStore.handleBlockStreamEvent({ + type: 'chain-finalized', + block: makeBlockId(5), + checkpoint: makeCheckpointIdForBlock(5), + }); const tips = await tipsStore.getL2Tips(); expect(tips.proposed).toEqual(makeTip(5)); @@ -229,8 +242,16 @@ export function testL2TipsStore(makeTipsStore: () => Promise) { await tipsStore.handleBlockStreamEvent(await makeCheckpointedEvent(checkpoint2)); // Prove and finalize up to block 3 (checkpoint 1) - await tipsStore.handleBlockStreamEvent({ type: 'chain-proven', block: makeBlockId(3) }); - await tipsStore.handleBlockStreamEvent({ type: 'chain-finalized', block: makeBlockId(3) }); + await tipsStore.handleBlockStreamEvent({ + type: 'chain-proven', + block: makeBlockId(3), + checkpoint: makeCheckpointIdForBlock(3), + }); + await tipsStore.handleBlockStreamEvent({ + type: 'chain-finalized', + block: makeBlockId(3), + checkpoint: makeCheckpointIdForBlock(3), + }); // Blocks before finalized should be cleared expect(await tipsStore.getL2BlockHash(1)).toBeUndefined(); @@ -440,7 +461,11 @@ export function testL2TipsStore(makeTipsStore: () => Promise) { await tipsStore.handleBlockStreamEvent(await makeCheckpointedEvent(checkpoint1)); // Prove up to block 3 - await tipsStore.handleBlockStreamEvent({ type: 'chain-proven', block: makeBlockId(3) }); + await tipsStore.handleBlockStreamEvent({ + type: 'chain-proven', + block: makeBlockId(3), + checkpoint: makeCheckpointIdForBlock(3), + }); let tips = await tipsStore.getL2Tips(); expect(tips.proposed).toEqual(makeTip(3)); @@ -530,15 +555,93 @@ export function testL2TipsStore(makeTipsStore: () => Promise) { expect(await tipsStore.getL2BlockHash(7)).not.toEqual(originalHash7); }); - // Regression test for #13142 - it('does not blow up when setting proven chain on an unseen block number', async () => { + // Regression test for #13142: proving an unseen block number (one with no local block->checkpoint + // mapping) must not blow up. With per-cursor checkpoint ids, the proven tip resolves to the + // checkpoint id carried by the event rather than falling back to checkpoint zero. + it('resolves the proven checkpoint from the carried id when setting proven on an unseen block number', async () => { await tipsStore.handleBlockStreamEvent({ type: 'blocks-added', blocks: [await makeBlock(5)] }); - await tipsStore.handleBlockStreamEvent({ type: 'chain-proven', block: makeBlockId(3) }); + // Block 3 has no local block->checkpoint mapping, but the event carries a real checkpoint id. + const carriedCheckpoint: CheckpointId = { number: CheckpointNumber(1), hash: new Fr(42).toString() }; + await tipsStore.handleBlockStreamEvent({ + type: 'chain-proven', + block: makeBlockId(3), + checkpoint: carriedCheckpoint, + }); const tips = await tipsStore.getL2Tips(); expect(tips.proposed).toEqual(makeTip(5)); expect(tips.proven.block).toEqual(makeTip(3)); - // No checkpoint for block 3 since it wasn't checkpointed + // Resolved from the carried id, not the (missing) local mapping. + expect(tips.proven.checkpoint).toEqual(carriedCheckpoint); + }); + + // proven/finalized resolve to the carried checkpoint id even when the block has no local + // block->checkpoint mapping (the cursor legitimately leads the locally-checkpointed frontier). + it('resolves proven and finalized checkpoints from carried ids without a local mapping', async () => { + await tipsStore.handleBlockStreamEvent({ type: 'blocks-added', blocks: [await makeBlock(7)] }); + const provenCheckpoint: CheckpointId = { number: CheckpointNumber(2), hash: new Fr(101).toString() }; + const finalizedCheckpoint: CheckpointId = { number: CheckpointNumber(1), hash: new Fr(100).toString() }; + await tipsStore.handleBlockStreamEvent({ + type: 'chain-proven', + block: makeBlockId(5), + checkpoint: provenCheckpoint, + }); + await tipsStore.handleBlockStreamEvent({ + type: 'chain-finalized', + block: makeBlockId(3), + checkpoint: finalizedCheckpoint, + }); + + const tips = await tipsStore.getL2Tips(); + expect(tips.proven.checkpoint).toEqual(provenCheckpoint); + expect(tips.finalized.checkpoint).toEqual(finalizedCheckpoint); + }); + + // Genuine corruption: a cursor points at a real (non-genesis) block that has a block hash but + // neither a stored per-cursor checkpoint id nor a block->checkpoint mapping. getL2Tips must throw + // loudly rather than silently report checkpoint zero. We reach this state by reaching past the + // event API to place the tip directly, since the normal writers always record an id. + it('throws when a cursor points at a real block with neither a stored id nor a mapping', async () => { + // blocks-added records the block hash for block 5 (so getBlockId succeeds) but no checkpoint id. + await tipsStore.handleBlockStreamEvent({ type: 'blocks-added', blocks: [await makeBlock(5)] }); + // Corrupt the proven cursor to point at block 5 without any id or mapping. + const internal = tipsStore as unknown as { setTip(tag: L2BlockTag, blockNumber: BlockNumber): Promise }; + await internal.setTip('proven', BlockNumber(5)); + + await expect(tipsStore.getL2Tips()).rejects.toThrow(/checkpoint/i); + }); + + // Skipped-history prune: a cursor clamped to a synced-but-unmapped prune target (no local + // block->checkpoint mapping, no resolvable id) must clamp to genesis and re-sync, NOT throw at read + // time. This is the exact failure mode that caused PR 1's scoped throw to be dropped. + it('clamps a checkpoint cursor to genesis (without throwing) when pruning to a synced-but-unmapped block', async () => { + // Checkpoint blocks 1-5 (checkpointed = block 5 / ckpt 1), then add uncheckpointed blocks 6-10. + const blocks1to5 = await Promise.all(times(5, i => makeBlock(i + 1))); + await tipsStore.handleBlockStreamEvent({ type: 'blocks-added', blocks: blocks1to5 }); + const checkpoint1 = await makeCheckpoint(1, blocks1to5); + await tipsStore.handleBlockStreamEvent(await makeCheckpointedEvent(checkpoint1)); + const blocks6to10 = await Promise.all(times(5, i => makeBlock(i + 6))); + await tipsStore.handleBlockStreamEvent({ type: 'blocks-added', blocks: blocks6to10 }); + + // Advance the proven cursor onto an uncheckpointed block (8) via a carried id, so it is AHEAD of + // the prune target and will be clamped, but block 7 (the prune target) has no local mapping. + await tipsStore.handleBlockStreamEvent({ + type: 'chain-proven', + block: makeBlockId(8), + checkpoint: { number: CheckpointNumber(2), hash: new Fr(202).toString() }, + }); + + // Prune to block 7, an uncheckpointed (unmapped) block, with a genesis checkpoint id on the event. + await tipsStore.handleBlockStreamEvent({ + type: 'chain-pruned', + block: makeBlockId(7), + checkpoint: { number: CheckpointNumber.ZERO, hash: GENESIS_CHECKPOINT_HEADER_HASH.toString() }, + }); + + // Must not throw; the proven cursor is clamped to genesis since block 7 has no resolvable id. + const tips = await tipsStore.getL2Tips(); + expect(tips.proposed).toEqual(makeTip(7)); + expect(tips.proven.block).toEqual(makeTip(0)); expect(tips.proven.checkpoint.number).toEqual(CheckpointNumber.ZERO); }); diff --git a/yarn-project/world-state/src/test/integration.test.ts b/yarn-project/world-state/src/test/integration.test.ts index a1c1b9d3dcd8..3748bd51c824 100644 --- a/yarn-project/world-state/src/test/integration.test.ts +++ b/yarn-project/world-state/src/test/integration.test.ts @@ -280,6 +280,7 @@ describe('world-state integration', () => { synchronizer.handleBlockStreamEvent({ type: 'chain-finalized', block: { number: backwardsFinalized, hash: '' }, + checkpoint: { number: CheckpointNumber(1), hash: new Fr(1).toString() }, }), ).resolves.not.toThrow(); From 0aeda09c3f20636c41e8ad8ecdb8744528d8eec9 Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Tue, 9 Jun 2026 15:33:41 -0300 Subject: [PATCH 2/8] refactor(p2p): drop the block->checkpoint mapping and checkpoint-object store from the tips store Now that every checkpoint-bearing tip in L2TipsStoreBase resolves its CheckpointId from a per-tip stored id (getTipCheckpoint/setTipCheckpoint), the block->checkpoint number mapping and the checkpoint-object store are redundant: they existed only to feed getCheckpointId. Remove both backing maps from the KV and memory stores along with their abstract methods, and collapse getCheckpointId to return the genesis id for block 0 and the stored per-tip id otherwise, throwing on a real block with no recorded id. The chain-pruned event now carries the source's confirmed checkpointed tip (checkpointed: L2TipId) instead of a bare CheckpointId. The prune handler clamps any checkpoint-bearing cursor that leads that tip down to it, always landing on a block with a valid recorded id; there is no longer a genesis-clamp or mapping lookup. handleChainFinalized prunes only block hashes below the lowest live tip. This is a breaking storage change (two PXE sub-stores removed); PXE_DATA_SCHEMA_VERSION is bumped so DatabaseVersionManager wipes pre-existing DBs rather than reading stale layouts. --- .../kv-store/src/stores/l2_tips_store.ts | 39 ------ yarn-project/p2p/src/client/p2p_client.ts | 2 +- .../block_synchronizer.test.ts | 20 ++- .../__snapshots__/L2TipsKVStore.json | 12 -- .../__snapshots__/opened_stores.json | 10 +- .../schema_tests.ts | 8 +- yarn-project/pxe/src/storage/metadata.ts | 2 +- .../src/block/l2_block_stream/interfaces.ts | 8 +- .../l2_block_stream/l2_block_stream.test.ts | 44 ++++--- .../block/l2_block_stream/l2_block_stream.ts | 2 +- .../l2_block_stream/l2_tips_memory_store.ts | 41 +----- .../l2_block_stream/l2_tips_store_base.ts | 122 +++--------------- .../block/test/l2_tips_store_test_suite.ts | 38 +++--- 13 files changed, 92 insertions(+), 256 deletions(-) diff --git a/yarn-project/kv-store/src/stores/l2_tips_store.ts b/yarn-project/kv-store/src/stores/l2_tips_store.ts index 61ad899f68ab..af582042e526 100644 --- a/yarn-project/kv-store/src/stores/l2_tips_store.ts +++ b/yarn-project/kv-store/src/stores/l2_tips_store.ts @@ -1,6 +1,5 @@ import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types'; import { type BlockHash, type CheckpointId, type L2BlockTag, L2TipsStoreBase } from '@aztec/stdlib/block'; -import { PublishedCheckpoint } from '@aztec/stdlib/checkpoint'; import type { AztecAsyncMap } from '../interfaces/map.js'; import type { AztecAsyncKVStore } from '../interfaces/store.js'; @@ -16,8 +15,6 @@ export class L2TipsKVStore extends L2TipsStoreBase { private readonly l2TipsStore: AztecAsyncMap; private readonly l2TipCheckpointsStore: AztecAsyncMap; private readonly l2BlockHashesStore: AztecAsyncMap; - private readonly l2BlockNumberToCheckpointNumberStore: AztecAsyncMap; - private readonly l2CheckpointStore: AztecAsyncMap; constructor( private store: AztecAsyncKVStore, @@ -28,10 +25,6 @@ export class L2TipsKVStore extends L2TipsStoreBase { this.l2TipsStore = store.openMap([namespace, 'l2_tips'].join('_')); this.l2TipCheckpointsStore = store.openMap([namespace, 'l2_tip_checkpoints'].join('_')); this.l2BlockHashesStore = store.openMap([namespace, 'l2_block_hashes'].join('_')); - this.l2BlockNumberToCheckpointNumberStore = store.openMap( - [namespace, 'l2_block_number_to_checkpoint_number'].join('_'), - ); - this.l2CheckpointStore = store.openMap([namespace, 'l2_checkpoint_store'].join('_')); } protected getTip(tag: L2BlockTag): Promise { @@ -68,38 +61,6 @@ export class L2TipsKVStore extends L2TipsStoreBase { } } - protected getCheckpointNumberForBlock(blockNumber: BlockNumber): Promise { - return this.l2BlockNumberToCheckpointNumberStore.getAsync(blockNumber); - } - - protected setCheckpointNumberForBlock(blockNumber: BlockNumber, checkpointNumber: CheckpointNumber): Promise { - return this.l2BlockNumberToCheckpointNumberStore.set(blockNumber, checkpointNumber); - } - - protected async deleteBlockToCheckpointBefore(blockNumber: BlockNumber): Promise { - for await (const key of this.l2BlockNumberToCheckpointNumberStore.keysAsync({ end: blockNumber })) { - await this.l2BlockNumberToCheckpointNumberStore.delete(key); - } - } - - protected async getCheckpoint(checkpointNumber: CheckpointNumber): Promise { - const buffer = await this.l2CheckpointStore.getAsync(checkpointNumber); - if (!buffer) { - return undefined; - } - return PublishedCheckpoint.fromBuffer(buffer); - } - - protected saveCheckpointData(checkpoint: PublishedCheckpoint): Promise { - return this.l2CheckpointStore.set(checkpoint.checkpoint.number, checkpoint.toBuffer()); - } - - protected async deleteCheckpointsBefore(checkpointNumber: CheckpointNumber): Promise { - for await (const key of this.l2CheckpointStore.keysAsync({ end: checkpointNumber })) { - await this.l2CheckpointStore.delete(key); - } - } - protected runInTransaction(fn: () => Promise): Promise { return this.store.transactionAsync(fn); } diff --git a/yarn-project/p2p/src/client/p2p_client.ts b/yarn-project/p2p/src/client/p2p_client.ts index c26aac46b805..656685f8ec23 100644 --- a/yarn-project/p2p/src/client/p2p_client.ts +++ b/yarn-project/p2p/src/client/p2p_client.ts @@ -174,7 +174,7 @@ export class P2PClient extends WithTracer implements P2P { break; case 'chain-pruned': this.txCollection.stopCollectingForBlocksAfter(event.block.number); - await this.handlePruneL2Blocks(event.block, event.checkpoint); + await this.handlePruneL2Blocks(event.block, event.checkpointed.checkpoint); break; case 'chain-checkpointed': break; diff --git a/yarn-project/pxe/src/block_synchronizer/block_synchronizer.test.ts b/yarn-project/pxe/src/block_synchronizer/block_synchronizer.test.ts index aae2b495cb88..58522550c87a 100644 --- a/yarn-project/pxe/src/block_synchronizer/block_synchronizer.test.ts +++ b/yarn-project/pxe/src/block_synchronizer/block_synchronizer.test.ts @@ -136,7 +136,10 @@ describe('BlockSynchronizer', () => { await synchronizer.handleBlockStreamEvent({ type: 'chain-pruned', block: makeL2BlockId(reorgBlock.number, reorgResponse.hash.toString()), - checkpoint: makeL2CheckpointId(CheckpointNumber.ZERO, GENESIS_CHECKPOINT_HEADER_HASH.toString()), + checkpointed: { + block: makeL2BlockId(BlockNumber.ZERO, GENESIS_BLOCK_HEADER_HASH.toString()), + checkpoint: makeL2CheckpointId(CheckpointNumber.ZERO, GENESIS_CHECKPOINT_HEADER_HASH.toString()), + }, }); // The anchor block should be updated to the reorg block header. @@ -230,7 +233,10 @@ describe('BlockSynchronizer', () => { await synchronizer.handleBlockStreamEvent({ type: 'chain-pruned', block: block3, - checkpoint: makeL2CheckpointId(CheckpointNumber.ZERO, GENESIS_CHECKPOINT_HEADER_HASH.toString()), + checkpointed: { + block: makeL2BlockId(BlockNumber.ZERO, GENESIS_BLOCK_HEADER_HASH.toString()), + checkpoint: makeL2CheckpointId(CheckpointNumber.ZERO, GENESIS_CHECKPOINT_HEADER_HASH.toString()), + }, }); // Rows at blocks 4 and 5 must be gone. @@ -305,7 +311,10 @@ describe('BlockSynchronizer', () => { await synchronizer.handleBlockStreamEvent({ type: 'chain-pruned', block: block1, - checkpoint: makeL2CheckpointId(CheckpointNumber.ZERO, GENESIS_CHECKPOINT_HEADER_HASH.toString()), + checkpointed: { + block: makeL2BlockId(BlockNumber.ZERO, GENESIS_BLOCK_HEADER_HASH.toString()), + checkpoint: makeL2CheckpointId(CheckpointNumber.ZERO, GENESIS_CHECKPOINT_HEADER_HASH.toString()), + }, }); // Blocks 2 and 3 deleted. @@ -448,7 +457,10 @@ describe('BlockSynchronizer', () => { await synchronizer.handleBlockStreamEvent({ type: 'chain-pruned', block: { number: BlockNumber(3), hash: '0x3' }, - checkpoint: makeL2CheckpointId(CheckpointNumber.ZERO, GENESIS_CHECKPOINT_HEADER_HASH.toString()), + checkpointed: { + block: makeL2BlockId(BlockNumber.ZERO, GENESIS_BLOCK_HEADER_HASH.toString()), + checkpoint: makeL2CheckpointId(CheckpointNumber.ZERO, GENESIS_CHECKPOINT_HEADER_HASH.toString()), + }, }); // Anchor should be unchanged diff --git a/yarn-project/pxe/src/storage/backwards_compatibility_tests/__snapshots__/L2TipsKVStore.json b/yarn-project/pxe/src/storage/backwards_compatibility_tests/__snapshots__/L2TipsKVStore.json index dbe46075ea67..5b4f71e66ce4 100644 --- a/yarn-project/pxe/src/storage/backwards_compatibility_tests/__snapshots__/L2TipsKVStore.json +++ b/yarn-project/pxe/src/storage/backwards_compatibility_tests/__snapshots__/L2TipsKVStore.json @@ -44,17 +44,5 @@ "key": "num:79", "value": "utf8:0x0000000000000000000000000000000000000000000000000000000000000053" } - ], - "pxe_l2_block_number_to_checkpoint_number": [ - { - "key": "num:179", - "value": "num:47" - } - ], - "pxe_l2_checkpoint_store": [ - { - "key": "num:47", - "value": "00000000000000000000000000000000000000000000000000000000000000020000000300000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000000000000000000130000000000000017000000000000000000000000000000000000001d000000000000000000000000000000000000000000000000000000000000001f0000000000000000000000000000002500000000000000000000000000000029000000000000000000000000000000000000000000000000000000000000002b00000001000000000000000000000000000000000000000000000000000000000000006b0000006d00000000000000000000000000000000000000000000000000000000000000710000007f000000000000000000000000000000000000000000000000000000000000008300000089000000000000000000000000000000000000000000000000000000000000008b0000009500000000000000000000000000000000000000000000000000000000000000970000009d00000000000000000000000000000000000000000000000000000000000000a300000000000000000000000000000000000000000000000000000000000000a700000000000000000000000000000000000000000000000000000000000000ad000000b3000000b500000000000000bf00000000000000000000000000000000000000c100000000000000000000000000000000000000000000000000000000000000c5000000000000000000000000000000c7000000000000000000000000000000d300000000000000000000000000000000000000000000000000000000000000df00000000000000000000000000000000000000000000000000000000000000e3000000000000000000000000000000000000000000000000000000000000006500000067000000010100000000000000000000000000000000000000000000000000000000000000e500000000000000000000000000000000000000000000000000000000000000e90100000000000000000000000000000000000000000000000000000000000000ef0100000000000000000000000000000000000000000000000000000000000000f10100000000000000000000000000000000000000000000000000000000000000fb010000000000000000000000000000000000000000000000000000000000000101000000000000000000000000000000000000000000000000000000000000010701000000000000000000000000000000000000000000000000000000000000010d000000000000000000000000000000000000000000000000000000000000010f000000000000000000000000000000000000000000000000000000000000011500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000001000000020000000000000000000000000000000000000000000000000000000000000119000000000000000000000000000000000000000000000000000000000000011b000000000000000000000000000000000000000000000000000000000000012501000000000000000000000000000000000000000000000000000000000000013300000000000000000000000000000000000000000000000000000000000001370000000000000000000000000000000000000000000000000000000000000139000000000000000000000000000000000000000000000000000000000000013d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000014b000001510000002f0000000000000000000000000000000000000000000000000000000000000035000000000000000000000000000000000000000000000000000000000000003b00000042307830303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303433000000000000000000000000000000000000000000000000000000000000003d00000000" - } ] } diff --git a/yarn-project/pxe/src/storage/backwards_compatibility_tests/__snapshots__/opened_stores.json b/yarn-project/pxe/src/storage/backwards_compatibility_tests/__snapshots__/opened_stores.json index aa94a1ebd3a1..0f52df771e6c 100644 --- a/yarn-project/pxe/src/storage/backwards_compatibility_tests/__snapshots__/opened_stores.json +++ b/yarn-project/pxe/src/storage/backwards_compatibility_tests/__snapshots__/opened_stores.json @@ -1,5 +1,5 @@ { - "schemaVersion": 7, + "schemaVersion": 8, "stores": [ { "name": "address_book", @@ -89,14 +89,6 @@ "name": "pxe_l2_block_hashes", "kind": "map" }, - { - "name": "pxe_l2_block_number_to_checkpoint_number", - "kind": "map" - }, - { - "name": "pxe_l2_checkpoint_store", - "kind": "map" - }, { "name": "pxe_l2_tip_checkpoints", "kind": "map" diff --git a/yarn-project/pxe/src/storage/backwards_compatibility_tests/schema_tests.ts b/yarn-project/pxe/src/storage/backwards_compatibility_tests/schema_tests.ts index b56a5738d313..ebe619fbbd58 100644 --- a/yarn-project/pxe/src/storage/backwards_compatibility_tests/schema_tests.ts +++ b/yarn-project/pxe/src/storage/backwards_compatibility_tests/schema_tests.ts @@ -256,8 +256,8 @@ export const SCHEMA_TESTS: readonly SchemaTest[] = [ ); // `'blocks-added'` writes to `pxe_l2_tips` (proposed tag) and `pxe_l2_block_hashes`. - // `'chain-checkpointed'` writes to all four sub-stores: tips ('checkpointed' and 'proposedCheckpoint' tags), - // block-to-checkpoint mapping, and the checkpoint store. + // `'chain-checkpointed'` writes the tips ('checkpointed' and 'proposedCheckpoint' tags) and their checkpoint ids + // (`pxe_l2_tip_checkpoints`). await l2TipsStore.handleBlockStreamEvent({ type: 'blocks-added', blocks: [block] }); await l2TipsStore.handleBlockStreamEvent({ type: 'chain-checkpointed', @@ -278,10 +278,6 @@ export const SCHEMA_TESTS: readonly SchemaTest[] = [ kvStore.openMap('pxe_l2_tip_checkpoints'), ), pxe_l2_block_hashes: await snapshotMap(kvStore.openMap('pxe_l2_block_hashes')), - pxe_l2_block_number_to_checkpoint_number: await snapshotMap( - kvStore.openMap('pxe_l2_block_number_to_checkpoint_number'), - ), - pxe_l2_checkpoint_store: await snapshotMap(kvStore.openMap('pxe_l2_checkpoint_store')), }), }, diff --git a/yarn-project/pxe/src/storage/metadata.ts b/yarn-project/pxe/src/storage/metadata.ts index 4136adf327e2..e242d8c59327 100644 --- a/yarn-project/pxe/src/storage/metadata.ts +++ b/yarn-project/pxe/src/storage/metadata.ts @@ -1 +1 @@ -export const PXE_DATA_SCHEMA_VERSION = 7; +export const PXE_DATA_SCHEMA_VERSION = 8; diff --git a/yarn-project/stdlib/src/block/l2_block_stream/interfaces.ts b/yarn-project/stdlib/src/block/l2_block_stream/interfaces.ts index 0f157f797256..be3312fd15c0 100644 --- a/yarn-project/stdlib/src/block/l2_block_stream/interfaces.ts +++ b/yarn-project/stdlib/src/block/l2_block_stream/interfaces.ts @@ -1,6 +1,6 @@ import type { PublishedCheckpoint } from '../../checkpoint/published_checkpoint.js'; import type { L2Block } from '../l2_block.js'; -import type { CheckpointId, L2BlockId, L2Tips } from '../l2_block_source.js'; +import type { CheckpointId, L2BlockId, L2TipId, L2Tips } from '../l2_block_source.js'; /** Provides the current chain tips. Implemented by world-state, l2-tips-store, and AztecNode. */ export interface L2TipsProvider { @@ -30,11 +30,13 @@ export type L2BlockStreamEvent = | /** * Reports last correct block (new tip of the proposed chain). Note that this is not necessarily the anchor block * that will be used in the transaction - if the chain has already moved past the reorg, we'll also see blocks-added - * events that will push the anchor block forward. + * events that will push the anchor block forward. `block` is the prune target (the new proposed tip); `checkpointed` + * is the source's confirmed checkpointed tip (block and checkpoint id), used to clamp checkpoint-bearing cursors that + * lead it. */ { type: 'chain-pruned'; block: L2BlockId; - checkpoint: CheckpointId; + checkpointed: L2TipId; } | /** Reports new proven block. */ { type: 'chain-proven'; diff --git a/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.test.ts b/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.test.ts index bc9fb7b62ccf..f4f882355924 100644 --- a/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.test.ts +++ b/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.test.ts @@ -242,7 +242,7 @@ describe('L2BlockStream', () => { await blockStream.work(); expect(handler.events).toEqual([ - { type: 'chain-pruned', block: makeBlockId(36), checkpoint: makeCheckpointId(0) }, + { type: 'chain-pruned', block: makeBlockId(36), checkpointed: makeTipId(0) }, { type: 'blocks-added', blocks: times(9, i => makeBlock(i + 37)) }, ] satisfies L2BlockStreamEvent[]); }); @@ -323,7 +323,7 @@ describe('L2BlockStream', () => { expect(handler.events[0]).toEqual({ type: 'chain-pruned', block: makeBlockId(3), - checkpoint: makeCheckpointId(3), + checkpointed: makeTipId(3), }); }); @@ -392,9 +392,7 @@ describe('L2BlockStream', () => { // And then we reorg setRemoteTips(25, 25, 25, 10); await blockStream.work(); - expect(handler.events).toEqual([ - { type: 'chain-pruned', block: makeBlockId(25), checkpoint: makeCheckpointId(25) }, - ]); + expect(handler.events).toEqual([{ type: 'chain-pruned', block: makeBlockId(25), checkpointed: makeTipId(25) }]); }); // Regression test for the checkpoint-replay storm: pruning to an uncheckpointed block ahead of @@ -412,9 +410,7 @@ describe('L2BlockStream', () => { // The stream prunes the local proposed tip from 7 back to 6. setRemoteTips(6, 5); await blockStream.work(); - expect(handler.events).toEqual([ - { type: 'chain-pruned', block: makeBlockId(6), checkpoint: makeCheckpointId(5) }, - ]); + expect(handler.events).toEqual([{ type: 'chain-pruned', block: makeBlockId(6), checkpointed: makeTipId(5) }]); handler.clearEvents(); // The next sync must NOT re-emit any chain-checkpointed events: the checkpointed cursor was @@ -1095,7 +1091,9 @@ describe('L2BlockStream', () => { { type: 'chain-pruned', block: makeBlockId(6), - checkpoint: expect.objectContaining({ number: CheckpointNumber(2) }), + checkpointed: expect.objectContaining({ + checkpoint: expect.objectContaining({ number: CheckpointNumber(2) }), + }), }, ]); @@ -1163,7 +1161,9 @@ describe('L2BlockStream', () => { { type: 'chain-pruned', block: makeBlockId(6), - checkpoint: expect.objectContaining({ number: CheckpointNumber(2) }), + checkpointed: expect.objectContaining({ + checkpoint: expect.objectContaining({ number: CheckpointNumber(2) }), + }), }, ]); @@ -1229,7 +1229,9 @@ describe('L2BlockStream', () => { { type: 'chain-pruned', block: makeBlockId(3), - checkpoint: expect.objectContaining({ number: CheckpointNumber(1) }), + checkpointed: expect.objectContaining({ + checkpoint: expect.objectContaining({ number: CheckpointNumber(1) }), + }), }, ]); @@ -1284,7 +1286,9 @@ describe('L2BlockStream', () => { { type: 'chain-pruned', block: makeBlockId(0), - checkpoint: expect.objectContaining({ number: CheckpointNumber(0) }), + checkpointed: expect.objectContaining({ + checkpoint: expect.objectContaining({ number: CheckpointNumber(0) }), + }), }, ]); @@ -1344,7 +1348,9 @@ describe('L2BlockStream', () => { { type: 'chain-pruned', block: makeBlockId(0), - checkpoint: expect.objectContaining({ number: CheckpointNumber(0) }), + checkpointed: expect.objectContaining({ + checkpoint: expect.objectContaining({ number: CheckpointNumber(0) }), + }), }, ]); @@ -1451,7 +1457,9 @@ describe('L2BlockStream', () => { { type: 'chain-pruned', block: makeBlockId(6), - checkpoint: expect.objectContaining({ number: CheckpointNumber(2) }), + checkpointed: expect.objectContaining({ + checkpoint: expect.objectContaining({ number: CheckpointNumber(2) }), + }), }, ]); }); @@ -1492,7 +1500,9 @@ describe('L2BlockStream', () => { { type: 'chain-pruned', block: makeBlockId(3), - checkpoint: expect.objectContaining({ number: CheckpointNumber(1) }), + checkpointed: expect.objectContaining({ + checkpoint: expect.objectContaining({ number: CheckpointNumber(1) }), + }), }, ]); }); @@ -1578,7 +1588,9 @@ describe('L2BlockStream', () => { { type: 'chain-pruned', block: makeBlockId(6), - checkpoint: expect.objectContaining({ number: CheckpointNumber(2) }), + checkpointed: expect.objectContaining({ + checkpoint: expect.objectContaining({ number: CheckpointNumber(2) }), + }), }, ]); diff --git a/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.ts b/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.ts index 122f4aa55198..8e752d2d4d9b 100644 --- a/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.ts +++ b/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.ts @@ -105,7 +105,7 @@ export class L2BlockStream { await this.emitEvent({ type: 'chain-pruned', block: makeL2BlockId(latestBlockNumber, hash), - checkpoint: sourceTips.checkpointed.checkpoint, + checkpointed: sourceTips.checkpointed, }); } diff --git a/yarn-project/stdlib/src/block/l2_block_stream/l2_tips_memory_store.ts b/yarn-project/stdlib/src/block/l2_block_stream/l2_tips_memory_store.ts index 8b7c3c205252..08420b378077 100644 --- a/yarn-project/stdlib/src/block/l2_block_stream/l2_tips_memory_store.ts +++ b/yarn-project/stdlib/src/block/l2_block_stream/l2_tips_memory_store.ts @@ -1,6 +1,5 @@ -import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types'; +import type { BlockNumber } from '@aztec/foundation/branded-types'; -import type { PublishedCheckpoint } from '../../checkpoint/published_checkpoint.js'; import type { BlockHash } from '../block_hash.js'; import type { CheckpointId, L2BlockTag } from '../l2_block_source.js'; import { L2TipsStoreBase } from './l2_tips_store_base.js'; @@ -17,8 +16,6 @@ export class L2TipsMemoryStore extends L2TipsStoreBase { private readonly tips = new Map(); private readonly tipCheckpoints = new Map(); private readonly blockHashes = new Map(); - private readonly blockToCheckpoint = new Map(); - private readonly checkpoints = new Map(); protected getTip(tag: L2BlockTag): Promise { return Promise.resolve(this.tips.get(tag)); @@ -56,42 +53,6 @@ export class L2TipsMemoryStore extends L2TipsStoreBase { return Promise.resolve(); } - protected getCheckpointNumberForBlock(blockNumber: BlockNumber): Promise { - return Promise.resolve(this.blockToCheckpoint.get(blockNumber)); - } - - protected setCheckpointNumberForBlock(blockNumber: BlockNumber, checkpointNumber: CheckpointNumber): Promise { - this.blockToCheckpoint.set(blockNumber, checkpointNumber); - return Promise.resolve(); - } - - protected deleteBlockToCheckpointBefore(blockNumber: BlockNumber): Promise { - for (const key of this.blockToCheckpoint.keys()) { - if (key < blockNumber) { - this.blockToCheckpoint.delete(key); - } - } - return Promise.resolve(); - } - - protected getCheckpoint(checkpointNumber: CheckpointNumber): Promise { - return Promise.resolve(this.checkpoints.get(checkpointNumber)); - } - - protected saveCheckpointData(checkpoint: PublishedCheckpoint): Promise { - this.checkpoints.set(checkpoint.checkpoint.number, checkpoint); - return Promise.resolve(); - } - - protected deleteCheckpointsBefore(checkpointNumber: CheckpointNumber): Promise { - for (const key of this.checkpoints.keys()) { - if (key < checkpointNumber) { - this.checkpoints.delete(key); - } - } - return Promise.resolve(); - } - protected runInTransaction(fn: () => Promise): Promise { // Memory store doesn't need transactions - just execute immediately return fn(); diff --git a/yarn-project/stdlib/src/block/l2_block_stream/l2_tips_store_base.ts b/yarn-project/stdlib/src/block/l2_block_stream/l2_tips_store_base.ts index 7b0589db2cfc..6be9b2910f35 100644 --- a/yarn-project/stdlib/src/block/l2_block_stream/l2_tips_store_base.ts +++ b/yarn-project/stdlib/src/block/l2_block_stream/l2_tips_store_base.ts @@ -1,6 +1,5 @@ import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types'; -import type { PublishedCheckpoint } from '../../checkpoint/published_checkpoint.js'; import type { BlockHash } from '../block_hash.js'; import type { L2Block } from '../l2_block.js'; import { @@ -41,27 +40,6 @@ export abstract class L2TipsStoreBase implements L2BlockStreamEventHandler, L2Bl /** Deletes all block hashes for blocks before the given block number. */ protected abstract deleteBlockHashesBefore(blockNumber: BlockNumber): Promise; - /** Gets the checkpoint number for a given block number. */ - protected abstract getCheckpointNumberForBlock(blockNumber: BlockNumber): Promise; - - /** Sets the checkpoint number for a given block number. */ - protected abstract setCheckpointNumberForBlock( - blockNumber: BlockNumber, - checkpointNumber: CheckpointNumber, - ): Promise; - - /** Deletes all block-to-checkpoint mappings for blocks before the given block number. */ - protected abstract deleteBlockToCheckpointBefore(blockNumber: BlockNumber): Promise; - - /** Gets a checkpoint by its number. */ - protected abstract getCheckpoint(checkpointNumber: CheckpointNumber): Promise; - - /** Saves a checkpoint. */ - protected abstract saveCheckpointData(checkpoint: PublishedCheckpoint): Promise; - - /** Deletes all checkpoints before the given checkpoint number. */ - protected abstract deleteCheckpointsBefore(checkpointNumber: CheckpointNumber): Promise; - /** Runs the given function in a transaction. Memory stores can just execute immediately. */ protected abstract runInTransaction(fn: () => Promise): Promise; @@ -147,56 +125,21 @@ export abstract class L2TipsStoreBase implements L2BlockStreamEventHandler, L2Bl if (blockNumber === undefined || blockNumber === 0) { return this.genesisCheckpointId(); } - // Prefer the checkpoint id recorded for this cursor when it was last advanced. This lets a cursor - // resolve correctly even when it legitimately leads the locally-checkpointed frontier (skipped - // history / startingBlock), where no local block->checkpoint mapping exists. + // The checkpoint id recorded for this cursor when it was last advanced is the single source of truth. + // The writers (handleChainCheckpointed/Proven/Finalized/Pruned) always record an id alongside any + // non-genesis cursor advance, so a missing id on a real block is genuine store corruption. Fail loudly + // rather than silently reporting checkpoint zero, which would drive a checkpoint-replay storm. const storedCheckpoint = await this.getTipCheckpoint(tag); if (storedCheckpoint !== undefined) { return storedCheckpoint; } - // Fall back to the block->checkpoint mapping for stores written before per-cursor ids existed. - const checkpointNumber = await this.getCheckpointNumberForBlock(blockNumber); - if (checkpointNumber !== undefined) { - const checkpoint = await this.getCheckpoint(checkpointNumber); - if (!checkpoint) { - throw new Error(`Checkpoint not found for checkpoint number ${checkpointNumber}`); - } - return { number: checkpointNumber, hash: checkpoint.checkpoint.hash().toString() }; - } - // A cursor on a real (non-genesis) block with neither a stored id nor a mapping is genuine store - // corruption. The writers (handleChainCheckpointed/Proven/Finalized/Pruned) always record an id or - // clamp to genesis, so reaching here means the store was corrupted. Fail loudly rather than silently - // reporting checkpoint zero, which would drive a checkpoint-replay storm. - throw new Error( - `No checkpoint id recorded for ${tag} tip at block ${blockNumber} and no block->checkpoint mapping found; ` + - `the L2 tips store is corrupted`, - ); + throw new Error(`No checkpoint id recorded for ${tag} tip at block ${blockNumber}; the L2 tips store is corrupted`); } private genesisCheckpointId(): CheckpointId { return { number: CheckpointNumber.ZERO, hash: GENESIS_CHECKPOINT_HEADER_HASH.toString() }; } - /** - * Resolves the checkpoint id for a block from the local block->checkpoint mapping, or genesis for block - * 0. Returns undefined when the block is a real block with no local mapping (used by the prune handler - * to decide whether a clamped cursor can keep the prune target or must fall back to genesis). - */ - private async resolveCheckpointIdForBlock(blockNumber: BlockNumber): Promise { - if (blockNumber === 0) { - return this.genesisCheckpointId(); - } - const checkpointNumber = await this.getCheckpointNumberForBlock(blockNumber); - if (checkpointNumber === undefined) { - return undefined; - } - const checkpoint = await this.getCheckpoint(checkpointNumber); - if (!checkpoint) { - return undefined; - } - return { number: checkpointNumber, hash: checkpoint.checkpoint.hash().toString() }; - } - private async handleBlocksAdded(event: L2BlockStreamEvent): Promise { if (event.type !== 'blocks-added') { return; @@ -221,7 +164,6 @@ export abstract class L2TipsStoreBase implements L2BlockStreamEventHandler, L2Bl }; await this.saveTag('checkpointed', event.block); await this.setTipCheckpoint('checkpointed', checkpointId); - await this.saveCheckpoint(event.checkpoint); // proposedCheckpoint is always >= checkpointed. If checkpointed has caught up // or surpassed it, advance proposedCheckpoint to match. const proposedCheckpointBlock = await this.getBlockId('proposedCheckpoint'); @@ -239,27 +181,18 @@ export abstract class L2TipsStoreBase implements L2BlockStreamEventHandler, L2Bl await this.runInTransaction(async () => { // A prune is a rollback: the proposed tip moves to the prune target unconditionally, but // checkpoint-bearing cursors may only move backward. Forward-advancing them onto an - // uncheckpointed block leaves them on a block with no checkpoint mapping, which getCheckpointId - // would otherwise resolve to checkpoint zero and drive a replay storm. + // uncheckpointed block leaves them on a block with no recorded checkpoint id, which getCheckpointId + // would then throw on. await this.saveTag('proposed', event.block); - // For each checkpoint-bearing cursor clamped to the prune target, resolve a consistent checkpoint - // id so the cursor never sits on a real block with no resolvable id (which getCheckpointId would - // otherwise throw on). When the prune target is a confirmed boundary the node has mapped, derive - // its id from the local mapping; otherwise the prune target is a synced-but-unmapped block (the - // skipped-history case), so clamp the cursor to genesis instead — this re-syncs cleanly rather - // than bricking the read. We never throw here. - const targetCheckpointId = await this.resolveCheckpointIdForBlock(event.block.number); + // Clamp any checkpoint-bearing cursor that leads the source's confirmed checkpointed tip down to + // that tip. The event always carries a valid (block, id) pair for the checkpointed boundary, so the + // clamped cursor always resolves to a recorded id. for (const tag of ['checkpointed', 'proposedCheckpoint', 'proven'] as const) { const current = await this.getTip(tag); - if (current !== undefined && current > event.block.number) { - if (targetCheckpointId !== undefined) { - await this.saveTag(tag, event.block); - await this.setTipCheckpoint(tag, targetCheckpointId); - } else { - await this.setTip(tag, BlockNumber.ZERO); - await this.setTipCheckpoint(tag, this.genesisCheckpointId()); - } + if (current !== undefined && current > event.checkpointed.block.number) { + await this.saveTag(tag, event.checkpointed.block); + await this.setTipCheckpoint(tag, event.checkpointed.checkpoint); } } }); @@ -282,11 +215,10 @@ export abstract class L2TipsStoreBase implements L2BlockStreamEventHandler, L2Bl await this.runInTransaction(async () => { await this.saveTag('finalized', event.block); await this.setTipCheckpoint('finalized', event.checkpoint); - const finalizedCheckpointNumber = await this.getCheckpointNumberForBlock(event.block.number); - // Cap the deletion bound at the lowest live tip. This should always be the finalized tip, but - // we have hit bugs where this is not the case. Deleting the block hash, block-to-checkpoint mapping, - // or enclosing checkpoint object for a live tip would dangle subsequent `getBlockId`/`getCheckpointId` + // Prune block hashes below the lowest live tip. Cap the deletion bound at the lowest live tip rather + // than the finalized tip alone: this should always be the finalized tip, but we have hit bugs where + // this is not the case. Deleting the block hash for a live tip would dangle subsequent `getBlockId` // lookups and lock the block stream into an error loop. const tips = await Promise.all([ this.getTip('proposed'), @@ -297,18 +229,6 @@ export abstract class L2TipsStoreBase implements L2BlockStreamEventHandler, L2Bl const liveTipBlocks = tips.filter((t): t is BlockNumber => t !== undefined && t > 0); const safeBlockBound = BlockNumber(Math.min(event.block.number, ...liveTipBlocks)); await this.deleteBlockHashesBefore(safeBlockBound); - await this.deleteBlockToCheckpointBefore(safeBlockBound); - - if (finalizedCheckpointNumber !== undefined) { - const tipCheckpoints = await Promise.all(liveTipBlocks.map(b => this.getCheckpointNumberForBlock(b))); - const safeCheckpointBound = CheckpointNumber( - Math.min( - finalizedCheckpointNumber, - ...tipCheckpoints.filter((c): c is CheckpointNumber => c !== undefined && c > 0), - ), - ); - await this.deleteCheckpointsBefore(safeCheckpointBound); - } }); } @@ -318,14 +238,4 @@ export abstract class L2TipsStoreBase implements L2BlockStreamEventHandler, L2Bl await this.setBlockHash(block.number, block.hash); } } - - private async saveCheckpoint(publishedCheckpoint: PublishedCheckpoint): Promise { - const checkpoint = publishedCheckpoint.checkpoint; - const lastBlock = checkpoint.blocks.at(-1)!; - // Only store the mapping for the last block since tips only point to checkpoint boundaries - await Promise.all([ - this.setCheckpointNumberForBlock(lastBlock.number, checkpoint.number), - this.saveCheckpointData(publishedCheckpoint), - ]); - } } diff --git a/yarn-project/stdlib/src/block/test/l2_tips_store_test_suite.ts b/yarn-project/stdlib/src/block/test/l2_tips_store_test_suite.ts index 89b9990e9471..5fa08e97a339 100644 --- a/yarn-project/stdlib/src/block/test/l2_tips_store_test_suite.ts +++ b/yarn-project/stdlib/src/block/test/l2_tips_store_test_suite.ts @@ -271,7 +271,7 @@ export function testL2TipsStore(makeTipsStore: () => Promise) { await tipsStore.handleBlockStreamEvent({ type: 'chain-pruned', block: makeBlockId(5), - checkpoint: { number: CheckpointNumber.ZERO, hash: GENESIS_CHECKPOINT_HEADER_HASH.toString() }, + checkpointed: makeTipId(0), }); const tips = await tipsStore.getL2Tips(); @@ -295,7 +295,7 @@ export function testL2TipsStore(makeTipsStore: () => Promise) { await tipsStore.handleBlockStreamEvent({ type: 'chain-pruned', block: makeTip(0), - checkpoint: { number: CheckpointNumber.ZERO, hash: GENESIS_CHECKPOINT_HEADER_HASH.toString() }, + checkpointed: makeTipId(0), }); tips = await tipsStore.getL2Tips(); @@ -360,7 +360,7 @@ export function testL2TipsStore(makeTipsStore: () => Promise) { await tipsStore.handleBlockStreamEvent({ type: 'chain-pruned', block: makeBlockId(5), - checkpoint: { number: CheckpointNumber.ZERO, hash: GENESIS_CHECKPOINT_HEADER_HASH.toString() }, + checkpointed: makeTipId(5), }); tips = await tipsStore.getL2Tips(); @@ -424,7 +424,7 @@ export function testL2TipsStore(makeTipsStore: () => Promise) { await tipsStore.handleBlockStreamEvent({ type: 'chain-pruned', block: makeBlockId(3), - checkpoint: { number: CheckpointNumber.ZERO, hash: GENESIS_CHECKPOINT_HEADER_HASH.toString() }, + checkpointed: makeTipId(3), }); tips = await tipsStore.getL2Tips(); @@ -506,7 +506,7 @@ export function testL2TipsStore(makeTipsStore: () => Promise) { await tipsStore.handleBlockStreamEvent({ type: 'chain-pruned', block: makeBlockId(3), - checkpoint: { number: CheckpointNumber.ZERO, hash: GENESIS_CHECKPOINT_HEADER_HASH.toString() }, + checkpointed: makeTipId(3), }); tips = await tipsStore.getL2Tips(); @@ -611,10 +611,11 @@ export function testL2TipsStore(makeTipsStore: () => Promise) { await expect(tipsStore.getL2Tips()).rejects.toThrow(/checkpoint/i); }); - // Skipped-history prune: a cursor clamped to a synced-but-unmapped prune target (no local - // block->checkpoint mapping, no resolvable id) must clamp to genesis and re-sync, NOT throw at read - // time. This is the exact failure mode that caused PR 1's scoped throw to be dropped. - it('clamps a checkpoint cursor to genesis (without throwing) when pruning to a synced-but-unmapped block', async () => { + // Backward prune of a leading cursor: a checkpoint-bearing cursor that legitimately leads the source's + // confirmed checkpointed tip is clamped down to that tip, resolving to the id the prune event carries + // (never genesis, never a throw). This is the skipped-history shape where the cursor sits on a block + // ahead of the checkpointed frontier. + it('clamps a leading checkpoint cursor down to the source checkpointed tip carried by the prune event', async () => { // Checkpoint blocks 1-5 (checkpointed = block 5 / ckpt 1), then add uncheckpointed blocks 6-10. const blocks1to5 = await Promise.all(times(5, i => makeBlock(i + 1))); await tipsStore.handleBlockStreamEvent({ type: 'blocks-added', blocks: blocks1to5 }); @@ -623,26 +624,26 @@ export function testL2TipsStore(makeTipsStore: () => Promise) { const blocks6to10 = await Promise.all(times(5, i => makeBlock(i + 6))); await tipsStore.handleBlockStreamEvent({ type: 'blocks-added', blocks: blocks6to10 }); - // Advance the proven cursor onto an uncheckpointed block (8) via a carried id, so it is AHEAD of - // the prune target and will be clamped, but block 7 (the prune target) has no local mapping. + // Advance the proven cursor onto an uncheckpointed block (8) via a carried id, so it LEADS the + // source checkpointed tip (block 5) and will be clamped down to it on prune. await tipsStore.handleBlockStreamEvent({ type: 'chain-proven', block: makeBlockId(8), checkpoint: { number: CheckpointNumber(2), hash: new Fr(202).toString() }, }); - // Prune to block 7, an uncheckpointed (unmapped) block, with a genesis checkpoint id on the event. + // Prune to block 7, carrying the source's confirmed checkpointed tip (block 5 / ckpt 1). await tipsStore.handleBlockStreamEvent({ type: 'chain-pruned', block: makeBlockId(7), - checkpoint: { number: CheckpointNumber.ZERO, hash: GENESIS_CHECKPOINT_HEADER_HASH.toString() }, + checkpointed: makeTipId(5), }); - // Must not throw; the proven cursor is clamped to genesis since block 7 has no resolvable id. + // Must not throw; the proven cursor is clamped to the carried checkpointed tip, resolving to ckpt 1. const tips = await tipsStore.getL2Tips(); expect(tips.proposed).toEqual(makeTip(7)); - expect(tips.proven.block).toEqual(makeTip(0)); - expect(tips.proven.checkpoint.number).toEqual(CheckpointNumber.ZERO); + expect(tips.proven.block).toEqual(makeTip(5)); + expect(tips.proven.checkpoint.number).toEqual(CheckpointNumber(1)); }); it('keeps the checkpointed tip when pruning to an uncheckpointed block ahead of it', async () => { @@ -654,11 +655,12 @@ export function testL2TipsStore(makeTipsStore: () => Promise) { const blocks6to7 = await Promise.all(times(2, i => makeBlock(i + 6))); await tipsStore.handleBlockStreamEvent({ type: 'blocks-added', blocks: blocks6to7 }); // proposed = 7 - // Prune to block 6: an uncheckpointed block AHEAD of the checkpointed tip (block 5). + // Prune to block 6: an uncheckpointed block AHEAD of the checkpointed tip (block 5). The source + // checkpointed tip is still block 5 / ckpt 1, so the checkpointed cursor must not move. await tipsStore.handleBlockStreamEvent({ type: 'chain-pruned', block: makeBlockId(6), - checkpoint: makeCheckpointIdForBlock(5), + checkpointed: makeTipId(5), }); const tips = await tipsStore.getL2Tips(); From e112563a86bf7deb2a59e7b626bba5dd0250be4f Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Tue, 9 Jun 2026 15:48:22 -0300 Subject: [PATCH 3/8] refactor(stdlib): drop proposedCheckpoint from the local L2 tips provider The local L2-block-stream tips providers (world-state, l2-tips-store) no longer carry proposedCheckpoint. In those local stores it is degenerate (always equal to checkpointed) and no consumer reads it: the only reader, in p2p_client's maybeCallPrepareForSlot, was a dead branch that was always false because the local store never leads the checkpointed tip. L2TipsProvider.getL2Tips now returns LocalL2Tips (Omit), the L2TipsStoreBase stops maintaining the cursor, and the dead p2p reader is removed. L2BlockSource.getL2Tips is a separate interface and keeps the full L2Tips with proposedCheckpoint, which the sequencer and node still read from the archiver. --- .../p2p/src/client/p2p_client.test.ts | 6 --- yarn-project/p2p/src/client/p2p_client.ts | 4 +- .../__snapshots__/L2TipsKVStore.json | 8 --- .../schema_tests.ts | 3 +- .../stdlib/src/block/l2_block_source.ts | 7 +++ .../src/block/l2_block_stream/interfaces.ts | 4 +- .../l2_block_stream/l2_block_stream.test.ts | 9 +--- .../l2_block_stream/l2_tips_store_base.ts | 49 ++++++------------- .../block/test/l2_tips_store_test_suite.ts | 9 +--- yarn-project/txe/src/state_machine/index.ts | 5 +- .../server_world_state_synchronizer.test.ts | 1 - .../server_world_state_synchronizer.ts | 8 +-- 12 files changed, 34 insertions(+), 79 deletions(-) diff --git a/yarn-project/p2p/src/client/p2p_client.test.ts b/yarn-project/p2p/src/client/p2p_client.test.ts index ac08036b58a6..2d2e10aa6425 100644 --- a/yarn-project/p2p/src/client/p2p_client.test.ts +++ b/yarn-project/p2p/src/client/p2p_client.test.ts @@ -335,10 +335,6 @@ describe('P2P Client', () => { await expect(client.getL2Tips()).resolves.toEqual({ proposed: { number: BlockNumber(100), hash: expect.any(String) }, checkpointed: { block: { number: BlockNumber(100), hash: expect.any(String) }, checkpoint: anyCheckpoint }, - proposedCheckpoint: { - block: { number: BlockNumber(100), hash: expect.any(String) }, - checkpoint: anyCheckpoint, - }, proven: { block: { number: BlockNumber(90), hash: expect.any(String) }, checkpoint: anyCheckpoint }, finalized: { block: { number: BlockNumber(50), hash: expect.any(String) }, checkpoint: anyCheckpoint }, }); @@ -349,7 +345,6 @@ describe('P2P Client', () => { await expect(client.getL2Tips()).resolves.toEqual({ proposed: { number: BlockNumber(90), hash: expect.any(String) }, - proposedCheckpoint: { block: { number: BlockNumber(90), hash: expect.any(String) }, checkpoint: anyCheckpoint }, checkpointed: { block: { number: BlockNumber(90), hash: expect.any(String) }, checkpoint: anyCheckpoint }, proven: { block: { number: BlockNumber(90), hash: expect.any(String) }, checkpoint: anyCheckpoint }, finalized: { block: { number: BlockNumber(50), hash: expect.any(String) }, checkpoint: anyCheckpoint }, @@ -362,7 +357,6 @@ describe('P2P Client', () => { await expect(client.getL2Tips()).resolves.toEqual({ proposed: { number: BlockNumber(92), hash: expect.any(String) }, - proposedCheckpoint: { block: { number: BlockNumber(92), hash: expect.any(String) }, checkpoint: anyCheckpoint }, checkpointed: { block: { number: BlockNumber(92), hash: expect.any(String) }, checkpoint: anyCheckpoint }, proven: { block: { number: BlockNumber(90), hash: expect.any(String) }, checkpoint: anyCheckpoint }, finalized: { block: { number: BlockNumber(50), hash: expect.any(String) }, checkpoint: anyCheckpoint }, diff --git a/yarn-project/p2p/src/client/p2p_client.ts b/yarn-project/p2p/src/client/p2p_client.ts index 656685f8ec23..4d959a044e6b 100644 --- a/yarn-project/p2p/src/client/p2p_client.ts +++ b/yarn-project/p2p/src/client/p2p_client.ts @@ -19,8 +19,8 @@ import { type L2BlockSource, L2BlockStream, type L2BlockStreamEvent, - type L2Tips, type L2TipsStore, + type LocalL2Tips, } from '@aztec/stdlib/block'; import type { ContractDataSource } from '@aztec/stdlib/contract'; import { getTimestampForSlot } from '@aztec/stdlib/epoch-helpers'; @@ -148,7 +148,7 @@ export class P2PClient extends WithTracer implements P2P { this.p2pService.updateConfig(config); } - public getL2Tips(): Promise { + public getL2Tips(): Promise { return this.l2Tips.getL2Tips(); } diff --git a/yarn-project/pxe/src/storage/backwards_compatibility_tests/__snapshots__/L2TipsKVStore.json b/yarn-project/pxe/src/storage/backwards_compatibility_tests/__snapshots__/L2TipsKVStore.json index 5b4f71e66ce4..110116f9521c 100644 --- a/yarn-project/pxe/src/storage/backwards_compatibility_tests/__snapshots__/L2TipsKVStore.json +++ b/yarn-project/pxe/src/storage/backwards_compatibility_tests/__snapshots__/L2TipsKVStore.json @@ -8,10 +8,6 @@ "key": "utf8:proposed", "value": "num:179" }, - { - "key": "utf8:proposedCheckpoint", - "value": "num:71" - }, { "key": "utf8:proven", "value": "num:79" @@ -22,10 +18,6 @@ "key": "utf8:checkpointed", "value": "{\"number\":47,\"hash\":\"0x00a21eaf943186f15f609a8a76fb0bee7ce3e78c86ef95f5466614e59546eb19\"}" }, - { - "key": "utf8:proposedCheckpoint", - "value": "{\"number\":47,\"hash\":\"0x00a21eaf943186f15f609a8a76fb0bee7ce3e78c86ef95f5466614e59546eb19\"}" - }, { "key": "utf8:proven", "value": "{\"number\":47,\"hash\":\"0x0000000000000000000000000000000000000000000000000000000000000059\"}" diff --git a/yarn-project/pxe/src/storage/backwards_compatibility_tests/schema_tests.ts b/yarn-project/pxe/src/storage/backwards_compatibility_tests/schema_tests.ts index ebe619fbbd58..e6e6fc8ac71d 100644 --- a/yarn-project/pxe/src/storage/backwards_compatibility_tests/schema_tests.ts +++ b/yarn-project/pxe/src/storage/backwards_compatibility_tests/schema_tests.ts @@ -256,8 +256,7 @@ export const SCHEMA_TESTS: readonly SchemaTest[] = [ ); // `'blocks-added'` writes to `pxe_l2_tips` (proposed tag) and `pxe_l2_block_hashes`. - // `'chain-checkpointed'` writes the tips ('checkpointed' and 'proposedCheckpoint' tags) and their checkpoint ids - // (`pxe_l2_tip_checkpoints`). + // `'chain-checkpointed'` writes the 'checkpointed' tip and its checkpoint id (`pxe_l2_tip_checkpoints`). await l2TipsStore.handleBlockStreamEvent({ type: 'blocks-added', blocks: [block] }); await l2TipsStore.handleBlockStreamEvent({ type: 'chain-checkpointed', diff --git a/yarn-project/stdlib/src/block/l2_block_source.ts b/yarn-project/stdlib/src/block/l2_block_source.ts index 934bc65bc408..fe3a6175cef4 100644 --- a/yarn-project/stdlib/src/block/l2_block_source.ts +++ b/yarn-project/stdlib/src/block/l2_block_source.ts @@ -351,6 +351,13 @@ export type L2Tips = { finalized: L2TipId; }; +/** + * Tips of the L2 chain as tracked by a local provider (world-state, l2-tips-store). Omits + * `proposedCheckpoint`, which is degenerate in local stores (always equal to `checkpointed`) and + * is only meaningful on the archiver side via {@link L2BlockSource}. + */ +export type LocalL2Tips = Omit; + export const GENESIS_CHECKPOINT_HEADER_HASH = CheckpointHeader.empty().hash(); /** Identifies a block by number and hash. */ diff --git a/yarn-project/stdlib/src/block/l2_block_stream/interfaces.ts b/yarn-project/stdlib/src/block/l2_block_stream/interfaces.ts index be3312fd15c0..e9c0c217141d 100644 --- a/yarn-project/stdlib/src/block/l2_block_stream/interfaces.ts +++ b/yarn-project/stdlib/src/block/l2_block_stream/interfaces.ts @@ -1,10 +1,10 @@ import type { PublishedCheckpoint } from '../../checkpoint/published_checkpoint.js'; import type { L2Block } from '../l2_block.js'; -import type { CheckpointId, L2BlockId, L2TipId, L2Tips } from '../l2_block_source.js'; +import type { CheckpointId, L2BlockId, L2TipId, LocalL2Tips } from '../l2_block_source.js'; /** Provides the current chain tips. Implemented by world-state, l2-tips-store, and AztecNode. */ export interface L2TipsProvider { - getL2Tips(): Promise; + getL2Tips(): Promise; } /** Interface to the local view of the chain. Implemented by world-state and l2-tips-store. */ diff --git a/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.test.ts b/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.test.ts index f4f882355924..0bdc6e4a1c23 100644 --- a/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.test.ts +++ b/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.test.ts @@ -16,7 +16,7 @@ import { GENESIS_CHECKPOINT_HEADER_HASH, type L2BlockId, type L2BlockSource, - type L2Tips, + type LocalL2Tips, } from '../l2_block_source.js'; import type { L2BlockStreamEvent, L2BlockStreamEventHandler, L2BlockStreamLocalDataProvider } from './interfaces.js'; import { L2BlockStream } from './l2_block_stream.js'; @@ -1810,10 +1810,6 @@ class TestL2BlockStreamLocalDataProvider implements L2BlockStreamLocalDataProvid block: { number: BlockNumber.ZERO, hash: GENESIS_BLOCK_HEADER_HASH.toString() }, checkpoint: { number: CheckpointNumber.ZERO, hash: GENESIS_CHECKPOINT_HEADER_HASH.toString() }, }; - public proposedCheckpointed = { - block: { number: BlockNumber.ZERO, hash: GENESIS_BLOCK_HEADER_HASH.toString() }, - checkpoint: { number: CheckpointNumber.ZERO, hash: GENESIS_CHECKPOINT_HEADER_HASH.toString() }, - }; public proven = { block: { number: BlockNumber.ZERO, hash: GENESIS_BLOCK_HEADER_HASH.toString() }, checkpoint: { number: CheckpointNumber.ZERO, hash: GENESIS_CHECKPOINT_HEADER_HASH.toString() }, @@ -1829,11 +1825,10 @@ class TestL2BlockStreamLocalDataProvider implements L2BlockStreamLocalDataProvid ); } - public getL2Tips(): Promise { + public getL2Tips(): Promise { return Promise.resolve({ proposed: this.proposed, checkpointed: this.checkpointed, - proposedCheckpoint: this.proposedCheckpointed, proven: this.proven, finalized: this.finalized, }); diff --git a/yarn-project/stdlib/src/block/l2_block_stream/l2_tips_store_base.ts b/yarn-project/stdlib/src/block/l2_block_stream/l2_tips_store_base.ts index 6be9b2910f35..708222e71f8a 100644 --- a/yarn-project/stdlib/src/block/l2_block_stream/l2_tips_store_base.ts +++ b/yarn-project/stdlib/src/block/l2_block_stream/l2_tips_store_base.ts @@ -7,7 +7,7 @@ import { GENESIS_CHECKPOINT_HEADER_HASH, type L2BlockId, type L2BlockTag, - type L2Tips, + type LocalL2Tips, } from '../l2_block_source.js'; import type { L2BlockStreamEvent, L2BlockStreamEventHandler, L2BlockStreamLocalDataProvider } from './interfaces.js'; @@ -52,31 +52,26 @@ export abstract class L2TipsStoreBase implements L2BlockStreamEventHandler, L2Bl return this.getStoredBlockHash(number); } - public getL2Tips(): Promise { + public getL2Tips(): Promise { return this.runInTransaction(async () => { - const [proposedBlockId, finalizedBlockId, provenBlockId, checkpointedBlockId, proposedCheckpointBlockId] = - await Promise.all([ - this.getBlockId('proposed'), - this.getBlockId('finalized'), - this.getBlockId('proven'), - this.getBlockId('checkpointed'), - this.getBlockId('proposedCheckpoint'), - ]); - - const [finalizedCheckpointId, provenCheckpointId, checkpointedCheckpointId, proposedCheckpointId] = - await Promise.all([ - this.getCheckpointId('finalized'), - this.getCheckpointId('proven'), - this.getCheckpointId('checkpointed'), - this.getCheckpointId('proposedCheckpoint'), - ]); + const [proposedBlockId, finalizedBlockId, provenBlockId, checkpointedBlockId] = await Promise.all([ + this.getBlockId('proposed'), + this.getBlockId('finalized'), + this.getBlockId('proven'), + this.getBlockId('checkpointed'), + ]); + + const [finalizedCheckpointId, provenCheckpointId, checkpointedCheckpointId] = await Promise.all([ + this.getCheckpointId('finalized'), + this.getCheckpointId('proven'), + this.getCheckpointId('checkpointed'), + ]); return { proposed: proposedBlockId, finalized: { block: finalizedBlockId, checkpoint: finalizedCheckpointId }, proven: { block: provenBlockId, checkpoint: provenCheckpointId }, checkpointed: { block: checkpointedBlockId, checkpoint: checkpointedCheckpointId }, - proposedCheckpoint: { block: proposedCheckpointBlockId, checkpoint: proposedCheckpointId }, }; }); } @@ -164,13 +159,6 @@ export abstract class L2TipsStoreBase implements L2BlockStreamEventHandler, L2Bl }; await this.saveTag('checkpointed', event.block); await this.setTipCheckpoint('checkpointed', checkpointId); - // proposedCheckpoint is always >= checkpointed. If checkpointed has caught up - // or surpassed it, advance proposedCheckpoint to match. - const proposedCheckpointBlock = await this.getBlockId('proposedCheckpoint'); - if (event.block.number > proposedCheckpointBlock.number) { - await this.saveTag('proposedCheckpoint', event.block); - await this.setTipCheckpoint('proposedCheckpoint', checkpointId); - } }); } @@ -188,7 +176,7 @@ export abstract class L2TipsStoreBase implements L2BlockStreamEventHandler, L2Bl // Clamp any checkpoint-bearing cursor that leads the source's confirmed checkpointed tip down to // that tip. The event always carries a valid (block, id) pair for the checkpointed boundary, so the // clamped cursor always resolves to a recorded id. - for (const tag of ['checkpointed', 'proposedCheckpoint', 'proven'] as const) { + for (const tag of ['checkpointed', 'proven'] as const) { const current = await this.getTip(tag); if (current !== undefined && current > event.checkpointed.block.number) { await this.saveTag(tag, event.checkpointed.block); @@ -220,12 +208,7 @@ export abstract class L2TipsStoreBase implements L2BlockStreamEventHandler, L2Bl // than the finalized tip alone: this should always be the finalized tip, but we have hit bugs where // this is not the case. Deleting the block hash for a live tip would dangle subsequent `getBlockId` // lookups and lock the block stream into an error loop. - const tips = await Promise.all([ - this.getTip('proposed'), - this.getTip('proposedCheckpoint'), - this.getTip('checkpointed'), - this.getTip('proven'), - ]); + const tips = await Promise.all([this.getTip('proposed'), this.getTip('checkpointed'), this.getTip('proven')]); const liveTipBlocks = tips.filter((t): t is BlockNumber => t !== undefined && t > 0); const safeBlockBound = BlockNumber(Math.min(event.block.number, ...liveTipBlocks)); await this.deleteBlockHashesBefore(safeBlockBound); diff --git a/yarn-project/stdlib/src/block/test/l2_tips_store_test_suite.ts b/yarn-project/stdlib/src/block/test/l2_tips_store_test_suite.ts index 5fa08e97a339..4120976695d1 100644 --- a/yarn-project/stdlib/src/block/test/l2_tips_store_test_suite.ts +++ b/yarn-project/stdlib/src/block/test/l2_tips_store_test_suite.ts @@ -68,18 +68,11 @@ export function testL2TipsStore(makeTipsStore: () => Promise) { checkpoint: makeCheckpointIdForBlock(blockNumber), }); - const makeTips = ( - proposed: number, - proven: number, - finalized: number, - checkpointed: number = 0, - proposedCheckpoint: number = 0, - ) => ({ + const makeTips = (proposed: number, proven: number, finalized: number, checkpointed: number = 0) => ({ proposed: makeTip(proposed), proven: makeTipId(proven), finalized: makeTipId(finalized), checkpointed: makeTipId(checkpointed), - proposedCheckpoint: makeTipId(proposedCheckpoint), }); const makeCheckpoint = async (checkpointNumber: number, blocks: L2Block[]): Promise => { diff --git a/yarn-project/txe/src/state_machine/index.ts b/yarn-project/txe/src/state_machine/index.ts index 4e33242b6de0..7d3bd75e23c0 100644 --- a/yarn-project/txe/src/state_machine/index.ts +++ b/yarn-project/txe/src/state_machine/index.ts @@ -83,10 +83,7 @@ export class TXEStateMachine { public get l2TipsProvider(): L2TipsProvider { const node = this.node; return { - getL2Tips: async () => { - const tips = await node.getChainTips(); - return { ...tips, proposedCheckpoint: tips.checkpointed }; - }, + getL2Tips: () => node.getChainTips(), }; } 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 42f5471d609c..7e7291d68cd9 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 @@ -306,7 +306,6 @@ class TestWorldStateSynchronizer extends ServerWorldStateSynchronizer { checkpointed: makeTipId(this.latest), proven: makeTipId(this.proven), finalized: makeTipId(this.finalized), - proposedCheckpoint: makeTipId(this.latest), }); } } 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 f46fe2c1b584..fe47a6946a7c 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 @@ -14,7 +14,7 @@ import { type L2BlockStreamEvent, type L2BlockStreamEventHandler, type L2BlockStreamLocalDataProvider, - type L2Tips, + type LocalL2Tips, } from '@aztec/stdlib/block'; import { WorldStateRunningState, @@ -267,7 +267,7 @@ export class ServerWorldStateSynchronizer } /** Returns the latest L2 block number for each tip of the chain (latest, proven, finalized). */ - public async getL2Tips(): Promise { + public async getL2Tips(): Promise { const status = await this.merkleTreeDb.getStatusSummary(); const unfinalizedBlockHashPromise = this.getL2BlockHash(status.unfinalizedBlockNumber); const finalizedBlockHashPromise = this.getL2BlockHash(status.finalizedBlockNumber); @@ -294,10 +294,6 @@ export class ServerWorldStateSynchronizer block: { number: BlockNumber.ZERO, hash: initialBlockHash }, checkpoint: { number: INITIAL_CHECKPOINT_NUMBER, hash: genesisCheckpointHeaderHash }, }, - proposedCheckpoint: { - block: { number: BlockNumber.ZERO, hash: initialBlockHash }, - checkpoint: { number: INITIAL_CHECKPOINT_NUMBER, hash: genesisCheckpointHeaderHash }, - }, finalized: { block: { number: status.finalizedBlockNumber, hash: finalizedBlockHash ?? '' }, checkpoint: { number: INITIAL_CHECKPOINT_NUMBER, hash: genesisCheckpointHeaderHash }, From 2ff4423b221774a87061a411dad340dad2147a33 Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Tue, 9 Jun 2026 16:04:17 -0300 Subject: [PATCH 4/8] fix(p2p): bump p2p store schema version for the per-tip checkpoint id layout The L2 tips store dropped the persisted block->checkpoint mapping and now resolves checkpoint tips exclusively from per-tip checkpoint ids. An upgraded store that kept its old tips with an empty l2_tip_checkpoints map would make getL2Tips throw on every read with no way to self-heal, failing P2PClient startup. Bumping the schema version resets the store on upgrade. --- yarn-project/p2p/src/client/factory.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/yarn-project/p2p/src/client/factory.ts b/yarn-project/p2p/src/client/factory.ts index a331fae469c1..e74770416f9e 100644 --- a/yarn-project/p2p/src/client/factory.ts +++ b/yarn-project/p2p/src/client/factory.ts @@ -75,8 +75,10 @@ export async function createP2PClient( } const bindings = logger.getBindings(); - // Schema version 3: tx proofs are stored in a separate map from the tx data (see TxPoolV2Impl). - const store = deps.store ?? (await createStore(P2P_STORE_NAME, 3, config, bindings)); + // Schema version 4: L2 tips store resolves checkpoint tips from per-tip ids in l2_tip_checkpoints; the + // block->checkpoint mapping and checkpoint maps were dropped. Bumped to wipe stores whose tips predate + // per-tip ids, which would otherwise make getL2Tips throw on every read. + const store = deps.store ?? (await createStore(P2P_STORE_NAME, 4, config, bindings)); const archive = await createStore(P2P_ARCHIVE_STORE_NAME, 1, config, bindings); const peerStore = await createStore(P2P_PEER_STORE_NAME, 1, config, bindings); const attestationStore = await createStore(P2P_ATTESTATION_STORE_NAME, 2, config, bindings); From 69ddece9b347ba065d4f0c47d2443cd8e4af309e Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Tue, 9 Jun 2026 16:38:50 -0300 Subject: [PATCH 5/8] fix(p2p): clamp the proven tip to the source proven tip on prune The chain-pruned event carried only the source checkpointed tip, so a prune that rolled back the proven chain clamped the local proven cursor onto the checkpointed tip, transiently reporting unproven blocks as proven until the corrective chain-proven event landed at the end of the same sync iteration. Carry the source proven tip in the event and clamp each cursor to its own tip. --- .../block_synchronizer.test.ts | 16 ++++++ .../src/block/l2_block_stream/interfaces.ts | 6 +- .../l2_block_stream/l2_block_stream.test.ts | 38 +++++++++++-- .../block/l2_block_stream/l2_block_stream.ts | 1 + .../l2_block_stream/l2_tips_store_base.ts | 20 ++++--- .../block/test/l2_tips_store_test_suite.ts | 57 ++++++++++++++++++- 6 files changed, 123 insertions(+), 15 deletions(-) diff --git a/yarn-project/pxe/src/block_synchronizer/block_synchronizer.test.ts b/yarn-project/pxe/src/block_synchronizer/block_synchronizer.test.ts index 58522550c87a..bbd6d2ae9729 100644 --- a/yarn-project/pxe/src/block_synchronizer/block_synchronizer.test.ts +++ b/yarn-project/pxe/src/block_synchronizer/block_synchronizer.test.ts @@ -140,6 +140,10 @@ describe('BlockSynchronizer', () => { block: makeL2BlockId(BlockNumber.ZERO, GENESIS_BLOCK_HEADER_HASH.toString()), checkpoint: makeL2CheckpointId(CheckpointNumber.ZERO, GENESIS_CHECKPOINT_HEADER_HASH.toString()), }, + proven: { + block: makeL2BlockId(BlockNumber.ZERO, GENESIS_BLOCK_HEADER_HASH.toString()), + checkpoint: makeL2CheckpointId(CheckpointNumber.ZERO, GENESIS_CHECKPOINT_HEADER_HASH.toString()), + }, }); // The anchor block should be updated to the reorg block header. @@ -237,6 +241,10 @@ describe('BlockSynchronizer', () => { block: makeL2BlockId(BlockNumber.ZERO, GENESIS_BLOCK_HEADER_HASH.toString()), checkpoint: makeL2CheckpointId(CheckpointNumber.ZERO, GENESIS_CHECKPOINT_HEADER_HASH.toString()), }, + proven: { + block: makeL2BlockId(BlockNumber.ZERO, GENESIS_BLOCK_HEADER_HASH.toString()), + checkpoint: makeL2CheckpointId(CheckpointNumber.ZERO, GENESIS_CHECKPOINT_HEADER_HASH.toString()), + }, }); // Rows at blocks 4 and 5 must be gone. @@ -315,6 +323,10 @@ describe('BlockSynchronizer', () => { block: makeL2BlockId(BlockNumber.ZERO, GENESIS_BLOCK_HEADER_HASH.toString()), checkpoint: makeL2CheckpointId(CheckpointNumber.ZERO, GENESIS_CHECKPOINT_HEADER_HASH.toString()), }, + proven: { + block: makeL2BlockId(BlockNumber.ZERO, GENESIS_BLOCK_HEADER_HASH.toString()), + checkpoint: makeL2CheckpointId(CheckpointNumber.ZERO, GENESIS_CHECKPOINT_HEADER_HASH.toString()), + }, }); // Blocks 2 and 3 deleted. @@ -461,6 +473,10 @@ describe('BlockSynchronizer', () => { block: makeL2BlockId(BlockNumber.ZERO, GENESIS_BLOCK_HEADER_HASH.toString()), checkpoint: makeL2CheckpointId(CheckpointNumber.ZERO, GENESIS_CHECKPOINT_HEADER_HASH.toString()), }, + proven: { + block: makeL2BlockId(BlockNumber.ZERO, GENESIS_BLOCK_HEADER_HASH.toString()), + checkpoint: makeL2CheckpointId(CheckpointNumber.ZERO, GENESIS_CHECKPOINT_HEADER_HASH.toString()), + }, }); // Anchor should be unchanged diff --git a/yarn-project/stdlib/src/block/l2_block_stream/interfaces.ts b/yarn-project/stdlib/src/block/l2_block_stream/interfaces.ts index e9c0c217141d..5a0b8dc08f7a 100644 --- a/yarn-project/stdlib/src/block/l2_block_stream/interfaces.ts +++ b/yarn-project/stdlib/src/block/l2_block_stream/interfaces.ts @@ -31,12 +31,14 @@ export type L2BlockStreamEvent = * Reports last correct block (new tip of the proposed chain). Note that this is not necessarily the anchor block * that will be used in the transaction - if the chain has already moved past the reorg, we'll also see blocks-added * events that will push the anchor block forward. `block` is the prune target (the new proposed tip); `checkpointed` - * is the source's confirmed checkpointed tip (block and checkpoint id), used to clamp checkpoint-bearing cursors that - * lead it. + * and `proven` are the source's confirmed checkpointed and proven tips (each a block and checkpoint id). Each is used + * to clamp the corresponding local cursor when it leads the source tip, so a cursor never overshoots its own source + * frontier during a prune (the source guarantees proven <= checkpointed). */ { type: 'chain-pruned'; block: L2BlockId; checkpointed: L2TipId; + proven: L2TipId; } | /** Reports new proven block. */ { type: 'chain-proven'; diff --git a/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.test.ts b/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.test.ts index 0bdc6e4a1c23..d93380e47945 100644 --- a/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.test.ts +++ b/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.test.ts @@ -242,7 +242,7 @@ describe('L2BlockStream', () => { await blockStream.work(); expect(handler.events).toEqual([ - { type: 'chain-pruned', block: makeBlockId(36), checkpointed: makeTipId(0) }, + { type: 'chain-pruned', block: makeBlockId(36), checkpointed: makeTipId(0), proven: makeTipId(0) }, { type: 'blocks-added', blocks: times(9, i => makeBlock(i + 37)) }, ] satisfies L2BlockStreamEvent[]); }); @@ -324,6 +324,7 @@ describe('L2BlockStream', () => { type: 'chain-pruned', block: makeBlockId(3), checkpointed: makeTipId(3), + proven: makeTipId(0), }); }); @@ -392,7 +393,9 @@ describe('L2BlockStream', () => { // And then we reorg setRemoteTips(25, 25, 25, 10); await blockStream.work(); - expect(handler.events).toEqual([{ type: 'chain-pruned', block: makeBlockId(25), checkpointed: makeTipId(25) }]); + expect(handler.events).toEqual([ + { type: 'chain-pruned', block: makeBlockId(25), checkpointed: makeTipId(25), proven: makeTipId(25) }, + ]); }); // Regression test for the checkpoint-replay storm: pruning to an uncheckpointed block ahead of @@ -410,7 +413,9 @@ describe('L2BlockStream', () => { // The stream prunes the local proposed tip from 7 back to 6. setRemoteTips(6, 5); await blockStream.work(); - expect(handler.events).toEqual([{ type: 'chain-pruned', block: makeBlockId(6), checkpointed: makeTipId(5) }]); + expect(handler.events).toEqual([ + { type: 'chain-pruned', block: makeBlockId(6), checkpointed: makeTipId(5), proven: makeTipId(0) }, + ]); handler.clearEvents(); // The next sync must NOT re-emit any chain-checkpointed events: the checkpointed cursor was @@ -1094,6 +1099,9 @@ describe('L2BlockStream', () => { checkpointed: expect.objectContaining({ checkpoint: expect.objectContaining({ number: CheckpointNumber(2) }), }), + proven: expect.objectContaining({ + block: expect.objectContaining({ number: BlockNumber(0) }), + }), }, ]); @@ -1156,7 +1164,7 @@ describe('L2BlockStream', () => { await blockStream.work(); - // Should emit chain-pruned back to block 6 + // Should emit chain-pruned back to block 6, carrying the source proven tip (block 6 / ckpt 2) expect(handler.events).toEqual([ { type: 'chain-pruned', @@ -1164,6 +1172,10 @@ describe('L2BlockStream', () => { checkpointed: expect.objectContaining({ checkpoint: expect.objectContaining({ number: CheckpointNumber(2) }), }), + proven: expect.objectContaining({ + block: expect.objectContaining({ number: BlockNumber(6) }), + checkpoint: expect.objectContaining({ number: CheckpointNumber(2) }), + }), }, ]); @@ -1232,6 +1244,9 @@ describe('L2BlockStream', () => { checkpointed: expect.objectContaining({ checkpoint: expect.objectContaining({ number: CheckpointNumber(1) }), }), + proven: expect.objectContaining({ + block: expect.objectContaining({ number: BlockNumber(0) }), + }), }, ]); @@ -1289,6 +1304,9 @@ describe('L2BlockStream', () => { checkpointed: expect.objectContaining({ checkpoint: expect.objectContaining({ number: CheckpointNumber(0) }), }), + proven: expect.objectContaining({ + block: expect.objectContaining({ number: BlockNumber(0) }), + }), }, ]); @@ -1351,6 +1369,9 @@ describe('L2BlockStream', () => { checkpointed: expect.objectContaining({ checkpoint: expect.objectContaining({ number: CheckpointNumber(0) }), }), + proven: expect.objectContaining({ + block: expect.objectContaining({ number: BlockNumber(0) }), + }), }, ]); @@ -1460,6 +1481,9 @@ describe('L2BlockStream', () => { checkpointed: expect.objectContaining({ checkpoint: expect.objectContaining({ number: CheckpointNumber(2) }), }), + proven: expect.objectContaining({ + block: expect.objectContaining({ number: BlockNumber(0) }), + }), }, ]); }); @@ -1503,6 +1527,9 @@ describe('L2BlockStream', () => { checkpointed: expect.objectContaining({ checkpoint: expect.objectContaining({ number: CheckpointNumber(1) }), }), + proven: expect.objectContaining({ + block: expect.objectContaining({ number: BlockNumber(0) }), + }), }, ]); }); @@ -1591,6 +1618,9 @@ describe('L2BlockStream', () => { checkpointed: expect.objectContaining({ checkpoint: expect.objectContaining({ number: CheckpointNumber(2) }), }), + proven: expect.objectContaining({ + block: expect.objectContaining({ number: BlockNumber(0) }), + }), }, ]); diff --git a/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.ts b/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.ts index 8e752d2d4d9b..7d9a87cb97a5 100644 --- a/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.ts +++ b/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.ts @@ -106,6 +106,7 @@ export class L2BlockStream { type: 'chain-pruned', block: makeL2BlockId(latestBlockNumber, hash), checkpointed: sourceTips.checkpointed, + proven: sourceTips.proven, }); } diff --git a/yarn-project/stdlib/src/block/l2_block_stream/l2_tips_store_base.ts b/yarn-project/stdlib/src/block/l2_block_stream/l2_tips_store_base.ts index 708222e71f8a..37a576424b29 100644 --- a/yarn-project/stdlib/src/block/l2_block_stream/l2_tips_store_base.ts +++ b/yarn-project/stdlib/src/block/l2_block_stream/l2_tips_store_base.ts @@ -173,14 +173,20 @@ export abstract class L2TipsStoreBase implements L2BlockStreamEventHandler, L2Bl // would then throw on. await this.saveTag('proposed', event.block); - // Clamp any checkpoint-bearing cursor that leads the source's confirmed checkpointed tip down to - // that tip. The event always carries a valid (block, id) pair for the checkpointed boundary, so the - // clamped cursor always resolves to a recorded id. - for (const tag of ['checkpointed', 'proven'] as const) { + // Clamp each checkpoint-bearing cursor down to its OWN source tip when it leads it. Clamping the proven + // cursor onto the checkpointed tip would transiently report unproven blocks as proven (the source's proven + // tip can sit below its checkpointed tip after a proof-tx reorg), until the corrective chain-proven event + // lands at the end of the same sync iteration. The event carries a valid (block, id) pair for each + // boundary, so the clamped cursor always resolves to a recorded id. The source guarantees proven <= + // checkpointed, so clamping each cursor to its own tip preserves the local proven <= checkpointed invariant. + for (const { tag, sourceTip } of [ + { tag: 'checkpointed', sourceTip: event.checkpointed }, + { tag: 'proven', sourceTip: event.proven }, + ] as const) { const current = await this.getTip(tag); - if (current !== undefined && current > event.checkpointed.block.number) { - await this.saveTag(tag, event.checkpointed.block); - await this.setTipCheckpoint(tag, event.checkpointed.checkpoint); + if (current !== undefined && current > sourceTip.block.number) { + await this.saveTag(tag, sourceTip.block); + await this.setTipCheckpoint(tag, sourceTip.checkpoint); } } }); diff --git a/yarn-project/stdlib/src/block/test/l2_tips_store_test_suite.ts b/yarn-project/stdlib/src/block/test/l2_tips_store_test_suite.ts index 4120976695d1..7335058c452f 100644 --- a/yarn-project/stdlib/src/block/test/l2_tips_store_test_suite.ts +++ b/yarn-project/stdlib/src/block/test/l2_tips_store_test_suite.ts @@ -265,6 +265,7 @@ export function testL2TipsStore(makeTipsStore: () => Promise) { type: 'chain-pruned', block: makeBlockId(5), checkpointed: makeTipId(0), + proven: makeTipId(0), }); const tips = await tipsStore.getL2Tips(); @@ -289,6 +290,7 @@ export function testL2TipsStore(makeTipsStore: () => Promise) { type: 'chain-pruned', block: makeTip(0), checkpointed: makeTipId(0), + proven: makeTipId(0), }); tips = await tipsStore.getL2Tips(); @@ -354,6 +356,7 @@ export function testL2TipsStore(makeTipsStore: () => Promise) { type: 'chain-pruned', block: makeBlockId(5), checkpointed: makeTipId(5), + proven: makeTipId(0), }); tips = await tipsStore.getL2Tips(); @@ -418,6 +421,7 @@ export function testL2TipsStore(makeTipsStore: () => Promise) { type: 'chain-pruned', block: makeBlockId(3), checkpointed: makeTipId(3), + proven: makeTipId(0), }); tips = await tipsStore.getL2Tips(); @@ -500,6 +504,7 @@ export function testL2TipsStore(makeTipsStore: () => Promise) { type: 'chain-pruned', block: makeBlockId(3), checkpointed: makeTipId(3), + proven: makeTipId(3), }); tips = await tipsStore.getL2Tips(); @@ -625,14 +630,16 @@ export function testL2TipsStore(makeTipsStore: () => Promise) { checkpoint: { number: CheckpointNumber(2), hash: new Fr(202).toString() }, }); - // Prune to block 7, carrying the source's confirmed checkpointed tip (block 5 / ckpt 1). + // Prune to block 7, carrying the source's confirmed checkpointed and proven tips (both block 5 / ckpt 1 + // here, the source's proven tip having rolled back together with its checkpointed tip). await tipsStore.handleBlockStreamEvent({ type: 'chain-pruned', block: makeBlockId(7), checkpointed: makeTipId(5), + proven: makeTipId(5), }); - // Must not throw; the proven cursor is clamped to the carried checkpointed tip, resolving to ckpt 1. + // Must not throw; the proven cursor is clamped to the carried proven tip, resolving to ckpt 1. const tips = await tipsStore.getL2Tips(); expect(tips.proposed).toEqual(makeTip(7)); expect(tips.proven.block).toEqual(makeTip(5)); @@ -654,6 +661,7 @@ export function testL2TipsStore(makeTipsStore: () => Promise) { type: 'chain-pruned', block: makeBlockId(6), checkpointed: makeTipId(5), + proven: makeTipId(0), }); const tips = await tipsStore.getL2Tips(); @@ -661,4 +669,49 @@ export function testL2TipsStore(makeTipsStore: () => Promise) { expect(tips.checkpointed.block).toEqual(makeTip(5)); expect(tips.checkpointed.checkpoint.number).toEqual(CheckpointNumber(1)); // must NOT be zero }); + + // Per-cursor clamping on prune: when the source rolls back its proven tip below its checkpointed tip + // (e.g. a proof tx dropped by an L1 reorg), the prune event carries both source tips and each local + // cursor must clamp to its OWN source tip. Clamping the proven cursor onto the (higher) checkpointed + // tip would transiently report unproven blocks as proven until the corrective chain-proven event lands. + it('clamps the proven cursor to the source proven tip, separately from the checkpointed cursor, on prune', async () => { + // Checkpoint blocks 1-15 across three checkpoints (ckpt 1 = 1-5, ckpt 2 = 6-10, ckpt 3 = 11-15). + const blocks1to5 = await Promise.all(times(5, i => makeBlock(i + 1))); + await tipsStore.handleBlockStreamEvent({ type: 'blocks-added', blocks: blocks1to5 }); + await tipsStore.handleBlockStreamEvent(await makeCheckpointedEvent(await makeCheckpoint(1, blocks1to5))); + const blocks6to10 = await Promise.all(times(5, i => makeBlock(i + 6))); + await tipsStore.handleBlockStreamEvent({ type: 'blocks-added', blocks: blocks6to10 }); + await tipsStore.handleBlockStreamEvent(await makeCheckpointedEvent(await makeCheckpoint(2, blocks6to10))); + const blocks11to15 = await Promise.all(times(5, i => makeBlock(i + 11))); + await tipsStore.handleBlockStreamEvent({ type: 'blocks-added', blocks: blocks11to15 }); + await tipsStore.handleBlockStreamEvent(await makeCheckpointedEvent(await makeCheckpoint(3, blocks11to15))); + + // Prove the whole chain up to block 15 (ckpt 3), so the local proven cursor leads both source tips below. + await tipsStore.handleBlockStreamEvent({ + type: 'chain-proven', + block: makeBlockId(15), + checkpoint: makeCheckpointIdForBlock(15), + }); + + let tips = await tipsStore.getL2Tips(); + expect(tips.checkpointed.block).toEqual(makeTip(15)); + expect(tips.proven.block).toEqual(makeTip(15)); + + // Prune arrives with the source's proven tip (block 5 / ckpt 1) BELOW its checkpointed tip (block 10 / + // ckpt 2) and below the local proven cursor (block 15). Each cursor must clamp to its own source tip. + await tipsStore.handleBlockStreamEvent({ + type: 'chain-pruned', + block: makeBlockId(10), + checkpointed: makeTipId(10), + proven: makeTipId(5), + }); + + tips = await tipsStore.getL2Tips(); + // The proven cursor lands exactly on the source proven tip, NOT on the (higher) checkpointed tip. + expect(tips.proven.block).toEqual(makeTip(5)); + expect(tips.proven.checkpoint.number).toEqual(CheckpointNumber(1)); + // The checkpointed cursor lands on the source checkpointed tip. + expect(tips.checkpointed.block).toEqual(makeTip(10)); + expect(tips.checkpointed.checkpoint.number).toEqual(CheckpointNumber(2)); + }); } From bc523fd0b6be284a294a97ce01321aa3e2cc7b79 Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Tue, 9 Jun 2026 17:23:52 -0300 Subject: [PATCH 6/8] refactor(world-state): stop fabricating checkpoint ids in the world-state tips provider The block stream's local data provider demanded full checkpoint-bearing tips, forcing world-state to fabricate genesis checkpoint ids and a checkpointed tip at block zero that violated the finalized <= checkpointed invariant. Narrow the provider contract to the tips the stream actually reads (checkpointed is only required when emitting checkpoint events) and have the stream fail loudly if checkpoint emission is enabled without one. World-state now reports only the proposed, proven, and finalized blocks it genuinely tracks; the full LocalL2Tips shape remains on L2TipsProvider for the p2p and pxe tips stores. --- .../src/block/l2_block_stream/interfaces.ts | 24 +++++- .../l2_block_stream/l2_block_stream.test.ts | 75 ++++++++++++++++++- .../block/l2_block_stream/l2_block_stream.ts | 14 +++- .../server_world_state_synchronizer.test.ts | 9 +-- .../server_world_state_synchronizer.ts | 23 ++---- 5 files changed, 117 insertions(+), 28 deletions(-) diff --git a/yarn-project/stdlib/src/block/l2_block_stream/interfaces.ts b/yarn-project/stdlib/src/block/l2_block_stream/interfaces.ts index 5a0b8dc08f7a..8a1ac9cb106f 100644 --- a/yarn-project/stdlib/src/block/l2_block_stream/interfaces.ts +++ b/yarn-project/stdlib/src/block/l2_block_stream/interfaces.ts @@ -7,8 +7,24 @@ export interface L2TipsProvider { getL2Tips(): Promise; } -/** Interface to the local view of the chain. Implemented by world-state and l2-tips-store. */ -export interface L2BlockStreamLocalDataProvider extends L2TipsProvider { +/** + * Minimal local view of the chain the block stream needs to drive sync. `checkpointed` is only required when the + * stream emits checkpoint events (i.e. `ignoreCheckpoints` is off). + */ +export type LocalChainTips = { + proposed: L2BlockId; + checkpointed?: { checkpoint: CheckpointId }; + proven: { block: L2BlockId }; + finalized: { block: L2BlockId }; +}; + +/** + * Interface to the local view of the chain. Implemented by world-state and l2-tips-store. Anything implementing + * {@link L2TipsProvider} also satisfies this contract structurally, since {@link LocalL2Tips} is assignable to + * {@link LocalChainTips}. + */ +export interface L2BlockStreamLocalDataProvider { + getL2Tips(): Promise; getL2BlockHash(number: number): Promise; } @@ -51,4 +67,6 @@ export type L2BlockStreamEvent = checkpoint: CheckpointId; }; -export type L2TipsStore = L2BlockStreamEventHandler & L2BlockStreamLocalDataProvider; +export type L2TipsStore = L2BlockStreamEventHandler & + L2TipsProvider & + Pick; diff --git a/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.test.ts b/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.test.ts index d93380e47945..1b17a151c4f5 100644 --- a/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.test.ts +++ b/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.test.ts @@ -1,6 +1,7 @@ import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types'; import { compactArray } from '@aztec/foundation/collection'; import { Fr } from '@aztec/foundation/curves/bn254'; +import type { Logger } from '@aztec/foundation/log'; import { jest } from '@jest/globals'; import { type MockProxy, mock } from 'jest-mock-extended'; @@ -18,7 +19,12 @@ import { type L2BlockSource, type LocalL2Tips, } from '../l2_block_source.js'; -import type { L2BlockStreamEvent, L2BlockStreamEventHandler, L2BlockStreamLocalDataProvider } from './interfaces.js'; +import type { + L2BlockStreamEvent, + L2BlockStreamEventHandler, + L2BlockStreamLocalDataProvider, + LocalChainTips, +} from './interfaces.js'; import { L2BlockStream } from './l2_block_stream.js'; import { L2TipsMemoryStore } from './l2_tips_memory_store.js'; @@ -1807,6 +1813,50 @@ describe('L2BlockStream', () => { ]); }); }); + + describe('local provider without checkpointed tip', () => { + let localData: TestLocalChainTipsProvider; + let handler: TestL2BlockStreamEventHandler; + + beforeEach(() => { + localData = new TestLocalChainTipsProvider(); + handler = new TestL2BlockStreamEventHandler(); + }); + + it('syncs blocks with ignoreCheckpoints when no checkpointed tip is provided', async () => { + setRemoteTips(5, 5); + const blockStream = new TestL2BlockStream(blockSource, localData, handler, undefined, { + batchSize: 10, + ignoreCheckpoints: true, + }); + + await blockStream.work(); + + // All 5 blocks are synced (one per checkpoint in the mock) and no checkpoint events are emitted. + expect(handler.events).toEqual([ + expectBlocksAdded([1]), + expectBlocksAdded([2]), + expectBlocksAdded([3]), + expectBlocksAdded([4]), + expectBlocksAdded([5]), + ]); + expect(handler.events.every(e => e.type === 'blocks-added')).toBe(true); + }); + + it('surfaces a loud error when checkpoint emission is enabled without a checkpointed tip', async () => { + setRemoteTips(5, 5); + const log = mock(); + const blockStream = new TestL2BlockStream(blockSource, localData, handler, log, { batchSize: 10 }); + + await blockStream.work(); + + expect(handler.events).toEqual([]); + expect(log.error).toHaveBeenCalledWith( + `Error processing block stream`, + expect.objectContaining({ message: expect.stringContaining('does not expose a checkpointed tip') }), + ); + }); + }); }); class TestL2BlockStreamEventHandler implements L2BlockStreamEventHandler { @@ -1865,6 +1915,29 @@ class TestL2BlockStreamLocalDataProvider implements L2BlockStreamLocalDataProvid } } +/** Local provider that omits `checkpointed`, mirroring world-state's `ignoreCheckpoints` configuration. */ +class TestLocalChainTipsProvider implements L2BlockStreamLocalDataProvider { + public readonly blockHashes: Record = {}; + + public proposed = { number: BlockNumber.ZERO, hash: GENESIS_BLOCK_HEADER_HASH.toString() }; + public proven = { block: { number: BlockNumber.ZERO, hash: GENESIS_BLOCK_HEADER_HASH.toString() } }; + public finalized = { block: { number: BlockNumber.ZERO, hash: GENESIS_BLOCK_HEADER_HASH.toString() } }; + + public getL2BlockHash(number: number): Promise { + return Promise.resolve( + number > this.proposed.number ? undefined : (this.blockHashes[number] ?? new Fr(number).toString()), + ); + } + + public getL2Tips(): Promise { + return Promise.resolve({ + proposed: this.proposed, + proven: this.proven, + finalized: this.finalized, + }); + } +} + class TestL2BlockStream extends L2BlockStream { public running = true; diff --git a/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.ts b/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.ts index 7d9a87cb97a5..3d5280e1684d 100644 --- a/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.ts +++ b/yarn-project/stdlib/src/block/l2_block_stream/l2_block_stream.ts @@ -70,6 +70,13 @@ export class L2BlockStream { const localTips = await this.localData.getL2Tips(); this.log.trace(`Running L2 block stream`, { sourceTips, localTips }); + if (!this.opts.ignoreCheckpoints && localTips.checkpointed === undefined) { + throw new Error( + 'Local data provider does not expose a checkpointed tip; checkpoint events require one ' + + '(set ignoreCheckpoints or provide checkpointed tips).', + ); + } + // Check if there was a reorg and emit a chain-pruned event if so. let latestBlockNumber = localTips.proposed.number; const sourceCache = new BlockHashCache([sourceTips.proposed]); @@ -123,7 +130,12 @@ export class L2BlockStream { } let nextBlockNumber = latestBlockNumber + 1; - let nextCheckpointToEmit = CheckpointNumber(localTips.checkpointed.checkpoint.number + 1); + // When checkpoints are ignored the local provider may omit `checkpointed`; in that case the fallback to + // CheckpointNumber.ZERO is harmless because `nextCheckpointToEmit` is never consumed for emission (Loop 1 and + // the startingBlock/skipFinalized adjustments below only feed checkpoint emission, which is gated off). + let nextCheckpointToEmit = CheckpointNumber( + (localTips.checkpointed?.checkpoint.number ?? CheckpointNumber.ZERO) + 1, + ); // When startingBlock is set, also skip ahead for checkpoints. if ( 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 7e7291d68cd9..07099fc6b150 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 @@ -297,15 +297,10 @@ class TestWorldStateSynchronizer extends ServerWorldStateSynchronizer { } public override getL2Tips() { - const makeTipId = (blockId: typeof this.latest) => ({ - block: blockId, - checkpoint: { number: CheckpointNumber.fromBlockNumber(blockId.number), hash: blockId.hash }, - }); return Promise.resolve({ proposed: this.latest, - checkpointed: makeTipId(this.latest), - proven: makeTipId(this.proven), - finalized: makeTipId(this.finalized), + proven: { block: this.proven }, + finalized: { block: this.finalized }, }); } } 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 fe47a6946a7c..5c0db14d7c6a 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,4 +1,3 @@ -import { INITIAL_CHECKPOINT_NUMBER } from '@aztec/constants'; import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types'; import type { Fr } from '@aztec/foundation/curves/bn254'; import { type Logger, createLogger } from '@aztec/foundation/log'; @@ -6,7 +5,6 @@ import { promiseWithResolvers } from '@aztec/foundation/promise'; import { elapsed } from '@aztec/foundation/timer'; import { type BlockHash, - GENESIS_CHECKPOINT_HEADER_HASH, type L2Block, type L2BlockId, type L2BlockSource, @@ -14,7 +12,7 @@ import { type L2BlockStreamEvent, type L2BlockStreamEventHandler, type L2BlockStreamLocalDataProvider, - type LocalL2Tips, + type LocalChainTips, } from '@aztec/stdlib/block'; import { WorldStateRunningState, @@ -266,8 +264,12 @@ export class ServerWorldStateSynchronizer return this.merkleTreeCommitted.getLeafValue(MerkleTreeId.ARCHIVE, BigInt(number)).then(leaf => leaf?.toString()); } - /** Returns the latest L2 block number for each tip of the chain (latest, proven, finalized). */ - public async getL2Tips(): Promise { + /** + * Returns the proposed, proven, and finalized block tips of the chain. World state drives its block stream with + * `ignoreCheckpoints`, so it does not track checkpointed blocks or checkpoints and omits `checkpointed` from the tips + * it reports. + */ + public async getL2Tips(): Promise { const status = await this.merkleTreeDb.getStatusSummary(); const unfinalizedBlockHashPromise = this.getL2BlockHash(status.unfinalizedBlockNumber); const finalizedBlockHashPromise = this.getL2BlockHash(status.finalizedBlockNumber); @@ -283,24 +285,13 @@ export class ServerWorldStateSynchronizer ]); const latestBlockId: L2BlockId = { number: status.unfinalizedBlockNumber, hash: unfinalizedBlockHash! }; - // World state doesn't track checkpointed blocks or checkpoints themselves. - // but we use a block stream so we need to provide 'local' L2Tips. - // We configure the block stream to ignore checkpoints and set checkpoint values to genesis here. - const genesisCheckpointHeaderHash = GENESIS_CHECKPOINT_HEADER_HASH.toString(); - const initialBlockHash = (await this.merkleTreeCommitted.getInitialHeader().hash()).toString(); return { proposed: latestBlockId, - checkpointed: { - block: { number: BlockNumber.ZERO, hash: initialBlockHash }, - checkpoint: { number: INITIAL_CHECKPOINT_NUMBER, hash: genesisCheckpointHeaderHash }, - }, finalized: { block: { number: status.finalizedBlockNumber, hash: finalizedBlockHash ?? '' }, - checkpoint: { number: INITIAL_CHECKPOINT_NUMBER, hash: genesisCheckpointHeaderHash }, }, proven: { block: { number: provenBlockNumber, hash: provenBlockHash ?? '' }, - checkpoint: { number: INITIAL_CHECKPOINT_NUMBER, hash: genesisCheckpointHeaderHash }, }, }; } From 15cc304325b9c462058e62f6d2cde4a84cb67c27 Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Tue, 9 Jun 2026 21:59:23 -0300 Subject: [PATCH 7/8] fix(prover-node): consume the reshaped chain-pruned and checkpoint-bearing events The CheckpointStore redesign landed on the merge train a prover-node consumer of chain-pruned written against the old event shape while this branch reshaped it: chain-pruned now carries checkpointed/proven tips instead of a bare checkpoint id, and chain-proven/chain-finalized carry checkpoint ids. Read the pruned checkpoint from event.checkpointed.checkpoint (same semantics as the old field) and update the test event constructors, including a hash() on the fake Checkpoint now that the tips store records checkpoint ids on checkpoint events. --- .../prover-node/src/prover-node.test.ts | 23 ++++++++++++++++--- yarn-project/prover-node/src/prover-node.ts | 2 +- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/yarn-project/prover-node/src/prover-node.test.ts b/yarn-project/prover-node/src/prover-node.test.ts index d3abd72b5cee..b9e23f2846c0 100644 --- a/yarn-project/prover-node/src/prover-node.test.ts +++ b/yarn-project/prover-node/src/prover-node.test.ts @@ -81,6 +81,12 @@ describe('ProverNode', () => { // ---------------- event dispatch ---------------- + /** Builds an L2TipId (block + checkpoint id) for block/checkpoint number `n`. */ + const makeTipId = (n: number) => ({ + block: { number: BlockNumber(n), hash: `0x0${n}` }, + checkpoint: { number: CheckpointNumber(n), hash: `0x0${n}` }, + }); + it('dispatches chain-checkpointed to handleCheckpointEvent', async () => { setupNotFullyProven(); const checkpoint = makeCheckpoint(1, 1, 1); @@ -100,8 +106,9 @@ describe('ProverNode', () => { // No registered checkpoints — nothing to prune. await proverNode.handleBlockStreamEvent({ type: 'chain-pruned', - checkpoint: { number: CheckpointNumber(0), hash: '0x00' }, block: { number: BlockNumber(0), hash: '0x00' }, + checkpointed: makeTipId(0), + proven: makeTipId(0), }); expect(sessionManager.onPrune).not.toHaveBeenCalled(); @@ -115,8 +122,9 @@ describe('ProverNode', () => { await proverNode.handleBlockStreamEvent({ type: 'chain-pruned', - checkpoint: { number: CheckpointNumber(1), hash: '0x01' }, block: { number: BlockNumber(1), hash: '0x01' }, + checkpointed: makeTipId(1), + proven: makeTipId(1), }); expect(sessionManager.onPrune).toHaveBeenCalledWith([EpochNumber(2)]); }); @@ -125,6 +133,7 @@ describe('ProverNode', () => { await proverNode.handleBlockStreamEvent({ type: 'chain-proven', block: { number: BlockNumber(7), hash: '0x07' }, + checkpoint: { number: CheckpointNumber(7), hash: '0x07' }, }); expect(publishingService.onChainProven).toHaveBeenCalledWith(BlockNumber(7)); }); @@ -149,6 +158,7 @@ describe('ProverNode', () => { await proverNode.handleBlockStreamEvent({ type: 'chain-finalized', block: { number: BlockNumber(1), hash: '0x01' }, + checkpoint: { number: CheckpointNumber(1), hash: '0x01' }, }); expect(cache.get(txHash)).toBeUndefined(); @@ -164,6 +174,7 @@ describe('ProverNode', () => { await proverNode.handleBlockStreamEvent({ type: 'chain-finalized', block: { number: BlockNumber(1), hash: '0x01' }, + checkpoint: { number: CheckpointNumber(1), hash: '0x01' }, }); expect(reapSpy.mock.calls.length).toBe(3); reapSpy.mockClear(); @@ -172,6 +183,7 @@ describe('ProverNode', () => { await proverNode.handleBlockStreamEvent({ type: 'chain-finalized', block: { number: BlockNumber(1), hash: '0x01' }, + checkpoint: { number: CheckpointNumber(1), hash: '0x01' }, }); expect(reapSpy).not.toHaveBeenCalled(); }); @@ -183,6 +195,7 @@ describe('ProverNode', () => { await proverNode.handleBlockStreamEvent({ type: 'chain-finalized', block: { number: BlockNumber(1), hash: '0x01' }, + checkpoint: { number: CheckpointNumber(1), hash: '0x01' }, }); expect(reapSpy).not.toHaveBeenCalled(); }); @@ -318,6 +331,7 @@ describe('ProverNode', () => { await proverNode.handleBlockStreamEvent({ type: 'chain-finalized', block: { number: BlockNumber(1), hash: '0x01' }, + checkpoint: { number: CheckpointNumber(1), hash: '0x01' }, }); expect(reapSpy).not.toHaveBeenCalled(); @@ -338,6 +352,7 @@ describe('ProverNode', () => { await proverNode.handleBlockStreamEvent({ type: 'chain-finalized', block: { number: BlockNumber(1), hash: '0x01' }, + checkpoint: { number: CheckpointNumber(1), hash: '0x01' }, }); expect(reapSpy.mock.calls.map(([e]) => Number(e))).toEqual([0, 1, 2]); @@ -372,8 +387,9 @@ describe('ProverNode', () => { sessionManager.onPrune.mockClear(); await proverNode.handleBlockStreamEvent({ type: 'chain-pruned', - checkpoint: { number: CheckpointNumber(0), hash: '0x00' }, block: { number: BlockNumber(0), hash: '0x00' }, + checkpointed: makeTipId(0), + proven: makeTipId(0), }); expect(sessionManager.onPrune).toHaveBeenCalledTimes(1); expect(sessionManager.onPrune).toHaveBeenCalledWith([EpochNumber(3)]); @@ -594,6 +610,7 @@ describe('ProverNode', () => { header: { slotNumber: SlotNumber(slot) }, archive: { root: archiveRoot }, blocks: [{ number: blockNumber, header: { hash: () => Promise.resolve('0x01') } }], + hash: () => new Fr(checkpointNumber), } as unknown as Checkpoint; } diff --git a/yarn-project/prover-node/src/prover-node.ts b/yarn-project/prover-node/src/prover-node.ts index 22c28e8c5bd4..9e5b7fbd7508 100644 --- a/yarn-project/prover-node/src/prover-node.ts +++ b/yarn-project/prover-node/src/prover-node.ts @@ -222,7 +222,7 @@ export class ProverNode implements L2BlockStreamEventHandler, ProverNodeApi, Tra await this.handleCheckpointEvent(event.checkpoint); break; case 'chain-pruned': - await this.handlePruneEvent(event.checkpoint); + await this.handlePruneEvent(event.checkpointed.checkpoint); break; case 'chain-proven': this.publishingService?.onChainProven(BlockNumber(event.block.number)); From c49c9e2801cd1c0494ecda6390893826129a572a Mon Sep 17 00:00:00 2001 From: Santiago Palladino Date: Wed, 10 Jun 2026 15:39:12 -0300 Subject: [PATCH 8/8] refactor(world-state): report unresolvable tip hashes as undefined instead of fabricating them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit World-state's getL2Tips fabricated values for hashes it could not resolve: an empty string for the proven/finalized tips and a non-null assertion for the proposed tip. The honest missing case is real — e.g. a proven tip ahead of the synced range has no archive leaf to resolve a hash from. LocalChainTips now carries a LocalL2BlockId with an optional hash and world-state returns resolved hashes as-is. Local tips stores are unaffected (LocalL2Tips with required hashes remains assignable to the widened shape), and the stream reads only block numbers from local tips. --- .../stdlib/src/block/l2_block_stream/interfaces.ts | 14 +++++++++++--- .../server_world_state_synchronizer.ts | 9 +++------ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/yarn-project/stdlib/src/block/l2_block_stream/interfaces.ts b/yarn-project/stdlib/src/block/l2_block_stream/interfaces.ts index 8a1ac9cb106f..e9e13deca97a 100644 --- a/yarn-project/stdlib/src/block/l2_block_stream/interfaces.ts +++ b/yarn-project/stdlib/src/block/l2_block_stream/interfaces.ts @@ -1,3 +1,5 @@ +import type { BlockNumber } from '@aztec/foundation/branded-types'; + import type { PublishedCheckpoint } from '../../checkpoint/published_checkpoint.js'; import type { L2Block } from '../l2_block.js'; import type { CheckpointId, L2BlockId, L2TipId, LocalL2Tips } from '../l2_block_source.js'; @@ -7,15 +9,21 @@ export interface L2TipsProvider { getL2Tips(): Promise; } +/** + * A block id reported by a local data provider, whose hash may be unknown when the provider cannot resolve it (e.g. + * world-state cannot resolve the hash of a proven tip ahead of its synced range). + */ +export type LocalL2BlockId = { number: BlockNumber; hash?: string }; + /** * Minimal local view of the chain the block stream needs to drive sync. `checkpointed` is only required when the * stream emits checkpoint events (i.e. `ignoreCheckpoints` is off). */ export type LocalChainTips = { - proposed: L2BlockId; + proposed: LocalL2BlockId; checkpointed?: { checkpoint: CheckpointId }; - proven: { block: L2BlockId }; - finalized: { block: L2BlockId }; + proven: { block: LocalL2BlockId }; + finalized: { block: LocalL2BlockId }; }; /** 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 5c0db14d7c6a..429f511a288d 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 @@ -6,7 +6,6 @@ import { elapsed } from '@aztec/foundation/timer'; import { type BlockHash, type L2Block, - type L2BlockId, type L2BlockSource, L2BlockStream, type L2BlockStreamEvent, @@ -283,15 +282,13 @@ export class ServerWorldStateSynchronizer finalizedBlockHashPromise, provenBlockHashPromise, ]); - const latestBlockId: L2BlockId = { number: status.unfinalizedBlockNumber, hash: unfinalizedBlockHash! }; - return { - proposed: latestBlockId, + proposed: { number: status.unfinalizedBlockNumber, hash: unfinalizedBlockHash }, finalized: { - block: { number: status.finalizedBlockNumber, hash: finalizedBlockHash ?? '' }, + block: { number: status.finalizedBlockNumber, hash: finalizedBlockHash }, }, proven: { - block: { number: provenBlockNumber, hash: provenBlockHash ?? '' }, + block: { number: provenBlockNumber, hash: provenBlockHash }, }, }; }