Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: World state synchronizer reorgs #9091

Merged
merged 1 commit into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions yarn-project/archiver/src/archiver/archiver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import {
type InboxLeaf,
type L1ToL2MessageSource,
type L2Block,
type L2BlockId,
type L2BlockL2Logs,
type L2BlockSource,
type L2LogsSource,
type L2Tips,
type LogFilter,
type LogType,
type TxEffect,
Expand Down Expand Up @@ -654,6 +656,32 @@ export class Archiver implements ArchiveSource {
getContractArtifact(address: AztecAddress): Promise<ContractArtifact | undefined> {
return this.store.getContractArtifact(address);
}

async getL2Tips(): Promise<L2Tips> {
const [latestBlockNumber, provenBlockNumber] = await Promise.all([
this.getBlockNumber(),
this.getProvenBlockNumber(),
] as const);

const [latestBlockHeader, provenBlockHeader] = await Promise.all([
latestBlockNumber > 0 ? this.getBlockHeader(latestBlockNumber) : undefined,
provenBlockNumber > 0 ? this.getBlockHeader(provenBlockNumber) : undefined,
] as const);

if (latestBlockNumber > 0 && !latestBlockHeader) {
throw new Error('Failed to retrieve latest block header');
}

if (provenBlockNumber > 0 && !provenBlockHeader) {
throw new Error('Failed to retrieve proven block header');
}

return {
latest: { number: latestBlockNumber, hash: latestBlockHeader?.hash().toString() } as L2BlockId,
proven: { number: provenBlockNumber, hash: provenBlockHeader?.hash().toString() } as L2BlockId,
finalized: { number: provenBlockNumber, hash: provenBlockHeader?.hash().toString() } as L2BlockId,
};
}
}

enum Operation {
Expand Down
2 changes: 2 additions & 0 deletions yarn-project/archiver/src/test/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export * from './mock_l2_block_source.js';
export * from './mock_l1_to_l2_message_source.js';
export * from './mock_archiver.js';
55 changes: 55 additions & 0 deletions yarn-project/archiver/src/test/mock_archiver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { type L1ToL2MessageSource, type L2Block, type L2BlockSource } from '@aztec/circuit-types';
import { type Fr } from '@aztec/circuits.js';

import { MockL1ToL2MessageSource } from './mock_l1_to_l2_message_source.js';
import { MockL2BlockSource } from './mock_l2_block_source.js';

/**
* A mocked implementation of the archiver that implements L2BlockSource and L1ToL2MessageSource.
*/
export class MockArchiver extends MockL2BlockSource implements L2BlockSource, L1ToL2MessageSource {
private messageSource = new MockL1ToL2MessageSource(0);

public setL1ToL2Messages(blockNumber: number, msgs: Fr[]) {
this.messageSource.setL1ToL2Messages(blockNumber, msgs);
}

getL1ToL2Messages(blockNumber: bigint): Promise<Fr[]> {
return this.messageSource.getL1ToL2Messages(blockNumber);
}

getL1ToL2MessageIndex(_l1ToL2Message: Fr, _startIndex: bigint): Promise<bigint | undefined> {
return this.messageSource.getL1ToL2MessageIndex(_l1ToL2Message, _startIndex);
}
}

/**
* A mocked implementation of the archiver with a set of precomputed blocks and messages.
*/
export class MockPrefilledArchiver extends MockArchiver {
private precomputed: L2Block[];

constructor(precomputed: L2Block[], messages: Fr[][]) {
super();
this.precomputed = precomputed.slice();
messages.forEach((msgs, i) => this.setL1ToL2Messages(i + 1, msgs));
}

public setPrefilledBlocks(blocks: L2Block[], messages: Fr[][]) {
for (const block of blocks) {
this.precomputed[block.number - 1] = block;
}
messages.forEach((msgs, i) => this.setL1ToL2Messages(blocks[i].number, msgs));
}

public override createBlocks(numBlocks: number) {
if (this.l2Blocks.length + numBlocks > this.precomputed.length) {
throw new Error(
`Not enough precomputed blocks to create ${numBlocks} more blocks (already at ${this.l2Blocks.length})`,
);
}

const fromBlock = this.l2Blocks.length;
this.addBlocks(this.precomputed.slice(fromBlock, fromBlock + numBlocks));
}
}
31 changes: 31 additions & 0 deletions yarn-project/archiver/src/test/mock_l1_to_l2_message_source.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { type L1ToL2MessageSource } from '@aztec/circuit-types';
import { type Fr } from '@aztec/circuits.js';

/**
* A mocked implementation of L1ToL2MessageSource to be used in tests.
*/
export class MockL1ToL2MessageSource implements L1ToL2MessageSource {
private messagesPerBlock = new Map<number, Fr[]>();

constructor(private blockNumber: number) {}

public setL1ToL2Messages(blockNumber: number, msgs: Fr[]) {
this.messagesPerBlock.set(blockNumber, msgs);
}

public setBlockNumber(blockNumber: number) {
this.blockNumber = blockNumber;
}

getL1ToL2Messages(blockNumber: bigint): Promise<Fr[]> {
return Promise.resolve(this.messagesPerBlock.get(Number(blockNumber)) ?? []);
}

getL1ToL2MessageIndex(_l1ToL2Message: Fr, _startIndex: bigint): Promise<bigint | undefined> {
throw new Error('Method not implemented.');
}

getBlockNumber(): Promise<number> {
return Promise.resolve(this.blockNumber);
}
}
59 changes: 42 additions & 17 deletions yarn-project/archiver/src/test/mock_l2_block_source.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,38 @@
import { L2Block, type L2BlockSource, type TxEffect, type TxHash, TxReceipt, TxStatus } from '@aztec/circuit-types';
import { L2Block, type L2BlockSource, type L2Tips, type TxHash, TxReceipt, TxStatus } from '@aztec/circuit-types';
import { EthAddress, type Header } from '@aztec/circuits.js';
import { createDebugLogger } from '@aztec/foundation/log';

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

/**
* A mocked implementation of L2BlockSource to be used in p2p tests.
* A mocked implementation of L2BlockSource to be used in tests.
*/
export class MockBlockSource implements L2BlockSource {
private l2Blocks: L2Block[] = [];
private txEffects: TxEffect[] = [];
export class MockL2BlockSource implements L2BlockSource {
protected l2Blocks: L2Block[] = [];

private provenEpochNumber: number = 0;
private provenBlockNumber: number = 0;

constructor(numBlocks = 100, private provenBlockNumber?: number) {
this.addBlocks(numBlocks);
}
private log = createDebugLogger('aztec:archiver:mock_l2_block_source');

public addBlocks(numBlocks: number) {
public createBlocks(numBlocks: number) {
for (let i = 0; i < numBlocks; i++) {
const blockNum = this.l2Blocks.length;
const block = L2Block.random(blockNum, blockNum);
const blockNum = this.l2Blocks.length + 1;
const block = L2Block.random(blockNum);
this.l2Blocks.push(block);
this.txEffects.push(...block.body.txEffects);
}

this.log.verbose(`Created ${numBlocks} blocks in the mock L2 block source`);
}

public addBlocks(blocks: L2Block[]) {
this.l2Blocks.push(...blocks);
this.log.verbose(`Added ${blocks.length} blocks to the mock L2 block source`);
}

public removeBlocks(numBlocks: number) {
this.l2Blocks = this.l2Blocks.slice(0, -numBlocks);
this.log.verbose(`Removed ${numBlocks} blocks from the mock L2 block source`);
}

public setProvenBlockNumber(provenBlockNumber: number) {
Expand Down Expand Up @@ -53,7 +64,7 @@ export class MockBlockSource implements L2BlockSource {
* @returns In this mock instance, returns the number of L2 blocks that we've mocked.
*/
public getBlockNumber() {
return Promise.resolve(this.l2Blocks.length - 1);
return Promise.resolve(this.l2Blocks.length);
}

public async getProvenBlockNumber(): Promise<number> {
Expand All @@ -70,7 +81,7 @@ export class MockBlockSource implements L2BlockSource {
* @returns The requested L2 block.
*/
public getBlock(number: number) {
return Promise.resolve(this.l2Blocks[number]);
return Promise.resolve(this.l2Blocks[number - 1]);
}

/**
Expand All @@ -82,13 +93,13 @@ export class MockBlockSource implements L2BlockSource {
public getBlocks(from: number, limit: number, proven?: boolean) {
return Promise.resolve(
this.l2Blocks
.slice(from, from + limit)
.slice(from - 1, from - 1 + limit)
.filter(b => !proven || this.provenBlockNumber === undefined || b.number <= this.provenBlockNumber),
);
}

getBlockHeader(number: number | 'latest'): Promise<Header | undefined> {
return Promise.resolve(this.l2Blocks.at(typeof number === 'number' ? number : -1)?.header);
return Promise.resolve(this.l2Blocks.at(typeof number === 'number' ? number - 1 : -1)?.header);
}

getBlocksForEpoch(epochNumber: bigint): Promise<L2Block[]> {
Expand All @@ -106,7 +117,7 @@ export class MockBlockSource implements L2BlockSource {
* @returns The requested tx effect.
*/
public getTxEffect(txHash: TxHash) {
const txEffect = this.txEffects.find(tx => tx.txHash.equals(txHash));
const txEffect = this.l2Blocks.flatMap(b => b.body.txEffects).find(tx => tx.txHash.equals(txHash));
return Promise.resolve(txEffect);
}

Expand Down Expand Up @@ -135,6 +146,20 @@ export class MockBlockSource implements L2BlockSource {
return Promise.resolve(undefined);
}

async getL2Tips(): Promise<L2Tips> {
const [latest, proven, finalized] = [
await this.getBlockNumber(),
await this.getProvenBlockNumber(),
await this.getProvenBlockNumber(),
] as const;

return {
latest: { number: latest, hash: this.l2Blocks[latest - 1]?.hash().toString() },
proven: { number: proven, hash: this.l2Blocks[proven - 1]?.hash().toString() },
finalized: { number: finalized, hash: this.l2Blocks[finalized - 1]?.hash().toString() },
};
}

getL2EpochNumber(): Promise<bigint> {
throw new Error('Method not implemented.');
}
Expand Down
7 changes: 0 additions & 7 deletions yarn-project/aztec/src/cli/aztec_start_options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,13 +193,6 @@ export const aztecStartOptions: { [key: string]: AztecStartOption[] } = {
defaultValue: undefined,
envVar: 'L1_PRIVATE_KEY',
},
{
flag: '--node.l2QueueSize <value>',
description: 'Size of queue of L2 blocks to store in world state',
defaultValue: 1000,
envVar: 'L2_QUEUE_SIZE',
parseVal: val => parseInt(val, 10),
},
{
flag: '--node.worldStateBlockCheckIntervalMS <value>',
description: 'Frequency in which to check for blocks in ms',
Expand Down
8 changes: 0 additions & 8 deletions yarn-project/circuit-types/src/interfaces/world_state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,6 @@ export interface WorldStateSynchronizer {
*/
syncImmediate(minBlockNumber?: number): Promise<number>;

/**
* Pauses the synchronizer, syncs to the target block number, forks world state, and resumes.
* @param targetBlockNumber - The block number to sync to.
* @param forkIncludeUncommitted - Whether to include uncommitted data in the fork.
* @returns The db forked at the requested target block number.
*/
syncImmediateAndFork(targetBlockNumber: number): Promise<MerkleTreeWriteOperations>;

/**
* Forks the current in-memory state based off the current committed state, and returns an instance that cannot modify the underlying data store.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './l2_block_downloader.js';
export * from './l2_block_stream.js';
Loading
Loading