Skip to content

Commit

Permalink
feat: updating archiver with new inbox (#5025)
Browse files Browse the repository at this point in the history
Fixes #4828
  • Loading branch information
benesjan authored Mar 11, 2024
1 parent 395b342 commit f6d17c9
Show file tree
Hide file tree
Showing 14 changed files with 496 additions and 34 deletions.
96 changes: 75 additions & 21 deletions yarn-project/archiver/src/archiver/archiver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@ import { AztecAddress } from '@aztec/foundation/aztec-address';
import { EthAddress } from '@aztec/foundation/eth-address';
import { Fr } from '@aztec/foundation/fields';
import { sleep } from '@aztec/foundation/sleep';
import { AvailabilityOracleAbi, ContractDeploymentEmitterAbi, InboxAbi, RollupAbi } from '@aztec/l1-artifacts';
import {
AvailabilityOracleAbi,
ContractDeploymentEmitterAbi,
InboxAbi,
NewInboxAbi,
RollupAbi,
} from '@aztec/l1-artifacts';

import { MockProxy, mock } from 'jest-mock-extended';
import { Chain, HttpTransport, Log, PublicClient, Transaction, encodeFunctionData, toHex } from 'viem';
Expand All @@ -13,11 +19,13 @@ import { ArchiverDataStore } from './archiver_store.js';
import { MemoryArchiverStore } from './memory_archiver_store/memory_archiver_store.js';

describe('Archiver', () => {
const rollupAddress = EthAddress.ZERO.toString();
const inboxAddress = EthAddress.ZERO.toString();
const registryAddress = EthAddress.ZERO.toString();
const availabilityOracleAddress = EthAddress.ZERO.toString();
const contractDeploymentEmitterAddress = '0x0000000000000000000000000000000000000001';
const rollupAddress = EthAddress.ZERO;
const inboxAddress = EthAddress.ZERO;
// TODO(#4492): Nuke this once the old inbox is purged
const newInboxAddress = EthAddress.ZERO;
const registryAddress = EthAddress.ZERO;
const availabilityOracleAddress = EthAddress.ZERO;
const contractDeploymentEmitterAddress = EthAddress.fromString('0x0000000000000000000000000000000000000001');
const blockNumbers = [1, 2, 3];
let publicClient: MockProxy<PublicClient<HttpTransport, Chain>>;
let archiverStore: ArchiverDataStore;
Expand All @@ -30,11 +38,12 @@ describe('Archiver', () => {
it('can start, sync and stop and handle l1 to l2 messages and logs', async () => {
const archiver = new Archiver(
publicClient,
EthAddress.fromString(rollupAddress),
EthAddress.fromString(availabilityOracleAddress),
EthAddress.fromString(inboxAddress),
EthAddress.fromString(registryAddress),
EthAddress.fromString(contractDeploymentEmitterAddress),
rollupAddress,
availabilityOracleAddress,
inboxAddress,
newInboxAddress,
registryAddress,
contractDeploymentEmitterAddress,
archiverStore,
1000,
);
Expand Down Expand Up @@ -82,11 +91,18 @@ describe('Archiver', () => {
publicClient.getLogs
.mockResolvedValueOnce(l1ToL2MessageAddedEvents.slice(0, 2).flat())
.mockResolvedValueOnce([]) // no messages to cancel
.mockResolvedValueOnce([makeLeafInsertedEvent(98n, 1n, 0n), makeLeafInsertedEvent(99n, 1n, 1n)])
.mockResolvedValueOnce([makeTxsPublishedEvent(101n, blocks[0].body.getTxsEffectsHash())])
.mockResolvedValueOnce([makeL2BlockProcessedEvent(101n, 1n)])
.mockResolvedValueOnce([makeContractDeploymentEvent(103n, blocks[0])]) // the first loop of the archiver ends here at block 2500
.mockResolvedValueOnce(l1ToL2MessageAddedEvents.slice(2, 4).flat())
.mockResolvedValueOnce(makeL1ToL2MessageCancelledEvents(2503n, l1ToL2MessagesToCancel))
.mockResolvedValueOnce([
makeLeafInsertedEvent(2504n, 2n, 0n),
makeLeafInsertedEvent(2505n, 2n, 1n),
makeLeafInsertedEvent(2505n, 2n, 2n),
makeLeafInsertedEvent(2506n, 3n, 1n),
])
.mockResolvedValueOnce([
makeTxsPublishedEvent(2510n, blocks[1].body.getTxsEffectsHash()),
makeTxsPublishedEvent(2520n, blocks[2].body.getTxsEffectsHash()),
Expand All @@ -110,6 +126,22 @@ describe('Archiver', () => {
latestBlockNum = await archiver.getBlockNumber();
expect(latestBlockNum).toEqual(3);

// New L1 to L2 messages
{
// Checks that I get correct amount of sequenced new messages for L2 blocks 1 and 2
let newL1ToL2Messages = await archiver.getNewL1ToL2Messages(1n);
expect(newL1ToL2Messages.length).toEqual(2);

newL1ToL2Messages = await archiver.getNewL1ToL2Messages(2n);
expect(newL1ToL2Messages.length).toEqual(3);

// Check that I cannot get messages for block 3 because there is a message gap (message with index 0 was not
// processed)
await expect(async () => {
await archiver.getNewL1ToL2Messages(3n);
}).rejects.toThrow(`L1 to L2 message gap found in block ${3}`);
}

// Check that only 2 messages (l1ToL2MessageAddedEvents[3][2] and l1ToL2MessageAddedEvents[3][3]) are pending.
// Other two (l1ToL2MessageAddedEvents[3][0..2]) were cancelled. And the previous messages were confirmed.
const expectedPendingEntryKeys = [
Expand Down Expand Up @@ -145,11 +177,12 @@ describe('Archiver', () => {
const numL2BlocksInTest = 2;
const archiver = new Archiver(
publicClient,
EthAddress.fromString(rollupAddress),
EthAddress.fromString(availabilityOracleAddress),
EthAddress.fromString(inboxAddress),
EthAddress.fromString(registryAddress),
EthAddress.fromString(contractDeploymentEmitterAddress),
rollupAddress,
availabilityOracleAddress,
inboxAddress,
newInboxAddress,
registryAddress,
contractDeploymentEmitterAddress,
archiverStore,
1000,
);
Expand Down Expand Up @@ -199,6 +232,7 @@ describe('Archiver', () => {
);
})
.mockResolvedValueOnce([])
.mockResolvedValueOnce([makeLeafInsertedEvent(66n, 1n, 0n), makeLeafInsertedEvent(68n, 1n, 1n)])
.mockResolvedValueOnce([
makeTxsPublishedEvent(70n, blocks[0].body.getTxsEffectsHash()),
makeTxsPublishedEvent(80n, blocks[1].body.getTxsEffectsHash()),
Expand Down Expand Up @@ -229,11 +263,12 @@ describe('Archiver', () => {
it('pads L1 to L2 messages', async () => {
const archiver = new Archiver(
publicClient,
EthAddress.fromString(rollupAddress),
EthAddress.fromString(availabilityOracleAddress),
EthAddress.fromString(inboxAddress),
EthAddress.fromString(registryAddress),
EthAddress.fromString(contractDeploymentEmitterAddress),
rollupAddress,
availabilityOracleAddress,
inboxAddress,
newInboxAddress,
registryAddress,
contractDeploymentEmitterAddress,
archiverStore,
1000,
);
Expand All @@ -255,6 +290,7 @@ describe('Archiver', () => {
),
)
.mockResolvedValueOnce([])
.mockResolvedValueOnce([])
.mockResolvedValueOnce([makeTxsPublishedEvent(101n, block.body.getTxsEffectsHash())])
.mockResolvedValueOnce([makeL2BlockProcessedEvent(101n, 1n)])
.mockResolvedValue([]);
Expand Down Expand Up @@ -378,6 +414,24 @@ function makeL1ToL2MessageCancelledEvents(l1BlockNum: bigint, entryKeys: string[
});
}

/**
* Makes fake L1ToL2 LeafInserted events for testing purposes.
* @param l1BlockNum - L1 block number.
* @param l2BlockNumber - The L2 block number of the leaf inserted.
* @returns LeafInserted event logs.
*/
function makeLeafInsertedEvent(l1BlockNum: bigint, l2BlockNumber: bigint, index: bigint) {
return {
blockNumber: l1BlockNum,
args: {
blockNumber: l2BlockNumber,
index,
value: Fr.random().toString(),
},
transactionHash: `0x${l1BlockNum}`,
} as Log<bigint, number, false, undefined, true, typeof NewInboxAbi, 'LeafInserted'>;
}

/**
* Makes a fake rollup tx for testing purposes.
* @param block - The L2Block.
Expand Down
43 changes: 42 additions & 1 deletion yarn-project/archiver/src/archiver/archiver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { EthAddress } from '@aztec/foundation/eth-address';
import { Fr } from '@aztec/foundation/fields';
import { DebugLogger, createDebugLogger } from '@aztec/foundation/log';
import { RunningPromise } from '@aztec/foundation/running-promise';
import { RollupAbi } from '@aztec/l1-artifacts';
import { ClassRegistererAddress } from '@aztec/protocol-contracts/class-registerer';
import { InstanceDeployerAddress } from '@aztec/protocol-contracts/instance-deployer';
import {
Expand All @@ -34,7 +35,7 @@ import {
ContractInstanceWithAddress,
} from '@aztec/types/contracts';

import { Chain, HttpTransport, PublicClient, createPublicClient, http } from 'viem';
import { Chain, HttpTransport, PublicClient, createPublicClient, getAddress, getContract, http } from 'viem';

import { ArchiverDataStore } from './archiver_store.js';
import { ArchiverConfig } from './config.js';
Expand All @@ -43,6 +44,7 @@ import {
retrieveBlockMetadataFromRollup,
retrieveNewCancelledL1ToL2Messages,
retrieveNewContractData,
retrieveNewL1ToL2Messages,
retrieveNewPendingL1ToL2Messages,
} from './data_retrieval.js';

Expand Down Expand Up @@ -72,6 +74,7 @@ export class Archiver implements ArchiveSource {
* @param publicClient - A client for interacting with the Ethereum node.
* @param rollupAddress - Ethereum address of the rollup contract.
* @param inboxAddress - Ethereum address of the inbox contract.
* @param newInboxAddress - Ethereum address of the new inbox contract.
* @param registryAddress - Ethereum address of the registry contract.
* @param contractDeploymentEmitterAddress - Ethereum address of the contractDeploymentEmitter contract.
* @param pollingIntervalMs - The interval for polling for L1 logs (in milliseconds).
Expand All @@ -83,6 +86,7 @@ export class Archiver implements ArchiveSource {
private readonly rollupAddress: EthAddress,
private readonly availabilityOracleAddress: EthAddress,
private readonly inboxAddress: EthAddress,
private readonly newInboxAddress: EthAddress,
private readonly registryAddress: EthAddress,
private readonly contractDeploymentEmitterAddress: EthAddress,
private readonly store: ArchiverDataStore,
Expand All @@ -109,11 +113,23 @@ export class Archiver implements ArchiveSource {
pollingInterval: config.viemPollingIntervalMS,
});

// TODO(#4492): Nuke this once the old inbox is purged
let newInboxAddress!: EthAddress;
{
const rollup = getContract({
address: getAddress(config.l1Contracts.rollupAddress.toString()),
abi: RollupAbi,
client: publicClient,
});
newInboxAddress = EthAddress.fromString(await rollup.read.NEW_INBOX());
}

const archiver = new Archiver(
publicClient,
config.l1Contracts.rollupAddress,
config.l1Contracts.availabilityOracleAddress,
config.l1Contracts.inboxAddress,
newInboxAddress,
config.l1Contracts.registryAddress,
config.l1Contracts.contractDeploymentEmitterAddress,
archiverStore,
Expand Down Expand Up @@ -163,6 +179,7 @@ export class Archiver implements ArchiveSource {

if (
currentL1BlockNumber <= lastL1Blocks.addedBlock &&
currentL1BlockNumber <= lastL1Blocks.newMessages &&
currentL1BlockNumber <= lastL1Blocks.addedMessages &&
currentL1BlockNumber <= lastL1Blocks.cancelledMessages
) {
Expand Down Expand Up @@ -192,6 +209,7 @@ export class Archiver implements ArchiveSource {

// ********** Events that are processed per L1 block **********

// TODO(#4492): Nuke the following when purging the old inbox
// Process l1ToL2Messages, these are consumed as time passes, not each block
const retrievedPendingL1ToL2Messages = await retrieveNewPendingL1ToL2Messages(
this.publicClient,
Expand Down Expand Up @@ -235,6 +253,20 @@ export class Archiver implements ArchiveSource {

// ********** Events that are processed per L2 block **********

const retrievedNewL1ToL2Messages = await retrieveNewL1ToL2Messages(
this.publicClient,
this.newInboxAddress,
blockUntilSynced,
lastL1Blocks.newMessages + 1n,
currentL1BlockNumber,
);
await this.store.addNewL1ToL2Messages(
retrievedNewL1ToL2Messages.retrievedData,
// -1n because the function expects the last block in which the message was emitted and not the one after next
// TODO(#4492): Check whether this could be cleaned up - `nextEthBlockNumber` value doesn't seem to be used much
retrievedNewL1ToL2Messages.nextEthBlockNumber - 1n,
);

// Read all data from chain and then write to our stores at the end
const nextExpectedL2BlockNum = BigInt((await this.store.getBlockNumber()) + 1);

Expand Down Expand Up @@ -583,6 +615,15 @@ export class Archiver implements ArchiveSource {
return this.store.getConfirmedL1ToL2Message(entryKey);
}

/**
* Gets new L1 to L2 message (to be) included in a given block.
* @param blockNumber - L2 block number to get messages for.
* @returns The L1 to L2 messages/leaves of the messages subtree (throws if not found).
*/
getNewL1ToL2Messages(blockNumber: bigint): Promise<Buffer[]> {
return this.store.getNewL1ToL2Messages(blockNumber);
}

getContractClassIds(): Promise<Fr[]> {
return this.store.getContractClassIds();
}
Expand Down
21 changes: 21 additions & 0 deletions yarn-project/archiver/src/archiver/archiver_store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
L2BlockL2Logs,
LogFilter,
LogType,
NewInboxLeaf,
TxEffect,
TxHash,
TxReceipt,
Expand All @@ -22,6 +23,9 @@ import { ContractClassPublic, ContractInstanceWithAddress } from '@aztec/types/c
export type ArchiverL1SynchPoint = {
/** The last L1 block that added a new L2 block. */
addedBlock: bigint;
/** The last L1 block that added messages from the new inbox. */
// TODO(#4492): Clean this up and fix the naming
newMessages: bigint;
/** The last L1 block that added pending messages */
addedMessages: bigint;
/** The last L1 block that cancelled messages */
Expand Down Expand Up @@ -90,11 +94,20 @@ export interface ArchiverDataStore {
blockNumber: number,
): Promise<boolean>;

/**
* Append new L1 to L2 messages to the store.
* @param messages - The L1 to L2 messages to be added to the store.
* @param lastMessageL1BlockNumber - The L1 block number in which the last message was emitted.
* @returns True if the operation is successful.
*/
addNewL1ToL2Messages(messages: NewInboxLeaf[], lastMessageL1BlockNumber: bigint): Promise<boolean>;

/**
* Append new pending L1 to L2 messages to the store.
* @param messages - The L1 to L2 messages to be added to the store.
* @param l1BlockNumber - The block number of the L1 block that added the messages.
* @returns True if the operation is successful.
* TODO(#4492): Nuke the following when purging the old inbox
*/
addPendingL1ToL2Messages(messages: L1ToL2Message[], l1BlockNumber: bigint): Promise<boolean>;

Expand All @@ -103,6 +116,7 @@ export interface ArchiverDataStore {
* @param entryKeys - The entry keys to be removed from the store.
* @param l1BlockNumber - The block number of the L1 block that cancelled the messages.
* @returns True if the operation is successful.
* TODO(#4492): Nuke the following when purging the old inbox
*/
cancelPendingL1ToL2EntryKeys(entryKeys: Fr[], l1BlockNumber: bigint): Promise<boolean>;

Expand All @@ -128,6 +142,13 @@ export interface ArchiverDataStore {
*/
getConfirmedL1ToL2Message(entryKey: Fr): Promise<L1ToL2Message>;

/**
* Gets new L1 to L2 message (to be) included in a given block.
* @param blockNumber - L2 block number to get messages for.
* @returns The L1 to L2 messages/leaves of the messages subtree (throws if not found).
*/
getNewL1ToL2Messages(blockNumber: bigint): Promise<Buffer[]>;

/**
* Gets up to `limit` amount of logs starting from `from`.
* @param from - Number of the L2 block to which corresponds the first logs to be returned.
Expand Down
Loading

0 comments on commit f6d17c9

Please sign in to comment.