Skip to content
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
3 changes: 2 additions & 1 deletion l1-contracts/src/core/interfaces/messagebridge/IInbox.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ interface IInbox {
uint256 recipientVersion,
uint32 deadline,
uint64 fee,
bytes32 content
bytes32 content,
bytes32 secretHash
);

event L1ToL2MessageCancelled(bytes32 indexed entryKey);
Expand Down
3 changes: 2 additions & 1 deletion l1-contracts/src/core/messagebridge/Inbox.sol
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ contract Inbox is IInbox {
message.recipient.version,
message.deadline,
message.fee,
message.content
message.content,
message.secretHash
);

return key;
Expand Down
6 changes: 4 additions & 2 deletions l1-contracts/test/Inbox.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ contract InboxTest is Test {
uint256 recipientVersion,
uint32 deadline,
uint64 fee,
bytes32 content
bytes32 content,
bytes32 secretHash
);

event L1ToL2MessageCancelled(bytes32 indexed entryKey);
Expand Down Expand Up @@ -65,7 +66,8 @@ contract InboxTest is Test {
_message.recipient.version,
_message.deadline,
_message.fee,
_message.content
_message.content,
_message.secretHash
);
// event we will get
bytes32 entryKey = inbox.sendL2Message{value: _message.fee}(
Expand Down
6 changes: 4 additions & 2 deletions l1-contracts/test/portals/TokenPortal.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ contract TokenPortalTest is Test {
uint256 recipientVersion,
uint32 deadline,
uint64 fee,
bytes32 content
bytes32 content,
bytes32 secretHash
);

Registry internal registry;
Expand Down Expand Up @@ -89,7 +90,8 @@ contract TokenPortalTest is Test {
expectedMessage.recipient.version,
expectedMessage.deadline,
expectedMessage.fee,
expectedMessage.content
expectedMessage.content,
expectedMessage.secretHash
);

// Perform op
Expand Down
7 changes: 4 additions & 3 deletions yarn-project/archiver/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
Archiver is a service which is used to fetch data on-chain data and present them in a nice-to-consume form.
The on-chain data specifically are the following events:
1. `L2BlockProcessed` event emitted on Rollup contract,
2. `UnverifiedData` event emitted on UnverifiedDataEmitter contract and
3. `ContractDeployment` event emitted on UnverifiedDataEmitter contract as well.
2. `UnverifiedData` event emitted on UnverifiedDataEmitter contract,
3. `ContractDeployment` event emitted on UnverifiedDataEmitter contract,
4. `MessageAdded` event emitted on Inbox contract,

The interfaces defining how the data can be consumed from the archiver are `L2BlockSource`, `UnverifiedDataSource` and `ContractDataSource`.

## Usage
To install dependencies and build the package run `yarn install` followed by `yarn build`.
To run test execute `yarn test`.

To start the service export `ETHEREUM_HOST` (defaults to `http://127.0.0.1:8545/`), `ARCHIVER_POLLING_INTERVAL` (defaults to `1000 ms`), `ROLLUP_CONTRACT_ADDRESS`, `UNVERIFIED_DATA_EMITTER_ADDRESS` environmental variables and start the service with `yarn start`.
To start the service export `ETHEREUM_HOST` (defaults to `http://127.0.0.1:8545/`), `ARCHIVER_POLLING_INTERVAL` (defaults to `1000 ms`), `ROLLUP_CONTRACT_ADDRESS`, `INBOX_CONTRACT_ADDRESS`, `UNVERIFIED_DATA_EMITTER_ADDRESS` environmental variables and start the service with `yarn start`.
33 changes: 32 additions & 1 deletion yarn-project/archiver/src/archiver/archiver.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { RollupAbi, UnverifiedDataEmitterAbi } from '@aztec/l1-artifacts';
import { InboxAbi, RollupAbi, UnverifiedDataEmitterAbi } from '@aztec/l1-artifacts';
import { ContractData, ContractPublicData, EncodedContractFunction, L2Block } from '@aztec/types';
import { MockProxy, mock } from 'jest-mock-extended';
import { Chain, HttpTransport, Log, PublicClient, Transaction, encodeFunctionData, toHex } from 'viem';
Expand All @@ -12,6 +12,7 @@ import { ArchiverDataStore, MemoryArchiverStore } from './archiver_store.js';

describe('Archiver', () => {
const rollupAddress = '0x0000000000000000000000000000000000000000';
const inboxAddress = '0x0000000000000000000000000000000000000000';
const unverifiedDataEmitterAddress = '0x0000000000000000000000000000000000000001';
let publicClient: MockProxy<PublicClient<HttpTransport, Chain>>;
let archiverStore: ArchiverDataStore;
Expand All @@ -25,6 +26,7 @@ describe('Archiver', () => {
const archiver = new Archiver(
publicClient,
EthAddress.fromString(rollupAddress),
EthAddress.fromString(inboxAddress),
EthAddress.fromString(unverifiedDataEmitterAddress),
archiverStore,
1000,
Expand All @@ -38,13 +40,16 @@ describe('Archiver', () => {
const rollupTxs = [1, 2, 3].map(makeRollupTx);

publicClient.getBlockNumber.mockResolvedValue(2500n);
// logs should be created in order of how archiver syncs.
publicClient.getLogs
.mockResolvedValueOnce([makeL2BlockProcessedEvent(100n, 1n)])
.mockResolvedValueOnce([makeUnverifiedDataEvent(102n, 1n)])
.mockResolvedValueOnce([makeContractDeployedEvent(104n, 1n)])
.mockResolvedValueOnce([makeL1ToL2MessageAddedEvent(101n)])
.mockResolvedValueOnce([makeL2BlockProcessedEvent(1100n, 2n), makeL2BlockProcessedEvent(1150n, 3n)])
.mockResolvedValueOnce([makeUnverifiedDataEvent(1100n, 2n)])
.mockResolvedValueOnce([makeContractDeployedEvent(1102n, 2n)])
.mockResolvedValueOnce([makeL1ToL2MessageAddedEvent(1101n)])
.mockResolvedValue([]);
rollupTxs.forEach(tx => publicClient.getTransaction.mockResolvedValueOnce(tx));

Expand All @@ -66,6 +71,9 @@ describe('Archiver', () => {
latestUnverifiedDataBlockNum = await archiver.getLatestUnverifiedDataBlockNum();
expect(latestUnverifiedDataBlockNum).toEqual(2);

// there are only 2 l1ToL2 messages in the store
expect((await archiver.getPendingL1ToL2Messages(10)).length).toEqual(2);

await archiver.stop();
}, 10_000);
});
Expand Down Expand Up @@ -129,6 +137,29 @@ function makeContractDeployedEvent(l1BlockNum: bigint, l2BlockNum: bigint) {
} as Log<bigint, number, undefined, typeof UnverifiedDataEmitterAbi, 'ContractDeployment'>;
}

/**
* Makes a fake L1ToL2 MessageAdded event for testing purposes.
* @param l1BlockNum - L1 block number.
* @returns An L2BlockProcessed event log.
*/
function makeL1ToL2MessageAddedEvent(l1BlockNum: bigint) {
return {
blockNumber: l1BlockNum,
args: {
sender: EthAddress.random().toString(),
senderChainId: 1n,
recipient: AztecAddress.random().toString(),
recipientVersion: 1n,
content: '0x' + randomBytes(32).toString('hex'),
secretHash: '0x' + randomBytes(32).toString('hex'),
deadline: 100,
fee: 1n,
entryKey: '0x' + randomBytes(32).toString('hex'),
},
transactionHash: `0x${l1BlockNum}`,
} as Log<bigint, number, undefined, typeof InboxAbi, 'MessageAdded'>;
}

/**
* Makes a fake rollup tx for testing purposes.
* @param blockNum - L2Block number.
Expand Down
33 changes: 30 additions & 3 deletions yarn-project/archiver/src/archiver/archiver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { DebugLogger, createDebugLogger } from '@aztec/foundation/log';
import { RunningPromise } from '@aztec/foundation/running-promise';
import { EthAddress } from '@aztec/foundation/eth-address';
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { INITIAL_L2_BLOCK_NUM } from '@aztec/types';
import { INITIAL_L2_BLOCK_NUM, L1ToL2Message, L1ToL2MessageSource } from '@aztec/types';
import {
ContractData,
ContractPublicData,
Expand All @@ -16,15 +16,20 @@ import {
import { Chain, HttpTransport, PublicClient, createPublicClient, http } from 'viem';
import { localhost } from 'viem/chains';
import { ArchiverConfig } from './config.js';
import { retrieveBlocks, retrieveNewContractData, retrieveUnverifiedData } from './data_retrieval.js';
import {
retrieveBlocks,
retrieveNewContractData,
retrieveUnverifiedData,
retrieveNewPendingL1ToL2Messages,
} from './data_retrieval.js';
import { ArchiverDataStore, MemoryArchiverStore } from './archiver_store.js';

/**
* Pulls L2 blocks in a non-blocking manner and provides interface for their retrieval.
* Responsible for handling robust L1 polling so that other components do not need to
* concern themselves with it.
*/
export class Archiver implements L2BlockSource, UnverifiedDataSource, ContractDataSource {
export class Archiver implements L2BlockSource, UnverifiedDataSource, ContractDataSource, L1ToL2MessageSource {
/**
* A promise in which we will be continually fetching new L2 blocks.
*/
Expand All @@ -39,6 +44,7 @@ export class Archiver implements L2BlockSource, UnverifiedDataSource, ContractDa
* Creates a new instance of the Archiver.
* @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 unverifiedDataEmitterAddress - Ethereum address of the unverifiedDataEmitter contract.
* @param pollingIntervalMs - The interval for polling for rollup logs (in milliseconds).
* @param store - An archiver data store for storage & retrieval of blocks, unverified data & contract data.
Expand All @@ -47,6 +53,7 @@ export class Archiver implements L2BlockSource, UnverifiedDataSource, ContractDa
constructor(
private readonly publicClient: PublicClient<HttpTransport, Chain>,
private readonly rollupAddress: EthAddress,
private readonly inboxAddress: EthAddress,
private readonly unverifiedDataEmitterAddress: EthAddress,
private readonly store: ArchiverDataStore,
private readonly pollingIntervalMs = 10_000,
Expand All @@ -68,6 +75,7 @@ export class Archiver implements L2BlockSource, UnverifiedDataSource, ContractDa
const archiver = new Archiver(
publicClient,
config.rollupContract,
config.inboxContract,
config.unverifiedDataEmitterContract,
archiverStore,
config.archiverPollingInterval,
Expand Down Expand Up @@ -130,6 +138,13 @@ export class Archiver implements L2BlockSource, UnverifiedDataSource, ContractDa
currentBlockNumber,
this.nextL2BlockFromBlock,
);
const retrievedPendingL1ToL2Messages = await retrieveNewPendingL1ToL2Messages(
this.publicClient,
this.inboxAddress,
blockUntilSynced,
currentBlockNumber,
this.nextL2BlockFromBlock,
);

if (retrievedBlocks.retrievedData.length === 0) {
return;
Expand All @@ -151,6 +166,9 @@ export class Archiver implements L2BlockSource, UnverifiedDataSource, ContractDa
}
});

// store l1 to l2 messages for which we have retrieved rollups
await this.store.addPendingL1ToL2Messages(retrievedPendingL1ToL2Messages.retrievedData);

// store retrieved rollup blocks
await this.store.addL2Blocks(retrievedBlocks.retrievedData);

Expand Down Expand Up @@ -259,4 +277,13 @@ export class Archiver implements L2BlockSource, UnverifiedDataSource, ContractDa
public getLatestUnverifiedDataBlockNum(): Promise<number> {
return this.store.getLatestUnverifiedDataBlockNum();
}

/**
* Gets the `take` amount of pending L1 to L2 messages.
* @param take - The number of messages to return.
* @returns The requested L1 to L2 messages.
*/
getPendingL1ToL2Messages(take: number): Promise<L1ToL2Message[]> {
Comment thread
rahul-kothari marked this conversation as resolved.
return this.store.getPendingL1ToL2Messages(take);
}
}
38 changes: 37 additions & 1 deletion yarn-project/archiver/src/archiver/archiver_store.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { ContractPublicData, L2Block, UnverifiedData, INITIAL_L2_BLOCK_NUM, ContractData } from '@aztec/types';
import {
ContractPublicData,
L2Block,
UnverifiedData,
INITIAL_L2_BLOCK_NUM,
ContractData,
L1ToL2Message,
} from '@aztec/types';
import { NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/circuits.js';
import { AztecAddress } from '@aztec/foundation/aztec-address';

/**
Expand All @@ -9,6 +17,8 @@ export interface ArchiverDataStore {
addL2Blocks(blocks: L2Block[]): Promise<boolean>;
getL2Blocks(from: number, take: number): Promise<L2Block[]>;
addUnverifiedData(data: UnverifiedData[]): Promise<boolean>;
addPendingL1ToL2Messages(messages: L1ToL2Message[]): Promise<boolean>;
getPendingL1ToL2Messages(take: number): Promise<L1ToL2Message[]>;
getUnverifiedData(from: number, take: number): Promise<UnverifiedData[]>;
addL2ContractPublicData(data: ContractPublicData[], blockNum: number): Promise<boolean>;
getL2ContractPublicData(contractAddress: AztecAddress): Promise<ContractPublicData | undefined>;
Expand Down Expand Up @@ -40,6 +50,11 @@ export class MemoryArchiverStore implements ArchiverDataStore {
*/
private contractPublicData: (ContractPublicData[] | undefined)[] = [];

/**
* An array containing all the pending L1 to L2 messages
*/
private pendingL1ToL2Messages: L1ToL2Message[] = [];

constructor() {}

/**
Expand All @@ -62,6 +77,16 @@ export class MemoryArchiverStore implements ArchiverDataStore {
return Promise.resolve(true);
}

/**
* Append new pending L1 to L2 messages to the store's list.
* @param messages - The L1 to L2 messages to be added to the store.
* @returns True if the operation is successful (always in this implementation).
*/
public addPendingL1ToL2Messages(messages: L1ToL2Message[]): Promise<boolean> {
this.pendingL1ToL2Messages.push(...messages);
return Promise.resolve(true);
}

/**
* Store new Contract Public Data from an L2 block to the store's list.
* @param data - List of contracts' data to be added.
Expand Down Expand Up @@ -91,6 +116,17 @@ export class MemoryArchiverStore implements ArchiverDataStore {
return Promise.resolve(this.l2Blocks.slice(startIndex, endIndex));
}

/**
* Gets the `take` amount of pending L1 to L2 messages.
* @param take - The number of messages to return (by default NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).
* @returns The requested L1 to L2 messages.
*/
public getPendingL1ToL2Messages(take: number = NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP): Promise<L1ToL2Message[]> {
// todo: @rahul https://github.com/AztecProtocol/aztec-packages/issues/529 - change this so that sequencer actually actually consumes messages sorted by fee or another value
// upon consumption, the messages are removed from the store
return Promise.resolve(this.pendingL1ToL2Messages.slice(0, take));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this decrease the size of the existing pendingMessages array? Or does it leave the sampled data there.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sampled for now - will remove in #529

}

/**
* Gets the `take` amount of unverified data starting from `from`.
* @param from - Number of the L2 block to which corresponds the first `unverifiedData` to be returned.
Expand Down
10 changes: 8 additions & 2 deletions yarn-project/archiver/src/archiver/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,18 @@ export interface ArchiverConfig extends L1Addresses {
* @returns The archiver configuration.
*/
export function getConfigEnvVars(): ArchiverConfig {
const { ETHEREUM_HOST, ARCHIVER_POLLING_INTERVAL, ROLLUP_CONTRACT_ADDRESS, UNVERIFIED_DATA_EMITTER_ADDRESS } =
process.env;
const {
ETHEREUM_HOST,
ARCHIVER_POLLING_INTERVAL,
ROLLUP_CONTRACT_ADDRESS,
INBOX_CONTRACT_ADDRESS,
UNVERIFIED_DATA_EMITTER_ADDRESS,
} = process.env;
return {
rpcUrl: ETHEREUM_HOST || 'http://127.0.0.1:8545/',
archiverPollingInterval: ARCHIVER_POLLING_INTERVAL ? +ARCHIVER_POLLING_INTERVAL : 1_000,
rollupContract: ROLLUP_CONTRACT_ADDRESS ? EthAddress.fromString(ROLLUP_CONTRACT_ADDRESS) : EthAddress.ZERO,
inboxContract: INBOX_CONTRACT_ADDRESS ? EthAddress.fromString(INBOX_CONTRACT_ADDRESS) : EthAddress.ZERO,
unverifiedDataEmitterContract: UNVERIFIED_DATA_EMITTER_ADDRESS
? EthAddress.fromString(UNVERIFIED_DATA_EMITTER_ADDRESS)
: EthAddress.ZERO,
Expand Down
Loading