Skip to content

Commit

Permalink
refactor: delete eth-log-handler
Browse files Browse the repository at this point in the history
  • Loading branch information
LHerskind committed Sep 18, 2024
1 parent a098d41 commit 1e4b1f9
Show file tree
Hide file tree
Showing 7 changed files with 208 additions and 273 deletions.
7 changes: 1 addition & 6 deletions yarn-project/archiver/src/archiver/archiver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,6 @@ interface MockRollupContractRead {
archiveAt: (args: readonly [bigint]) => Promise<`0x${string}`>;
}

class MockRollupContract {
constructor(public read: MockRollupContractRead, public address: `0x${string}`) {}
}

describe('Archiver', () => {
const rollupAddress = EthAddress.ZERO;
const inboxAddress = EthAddress.ZERO;
Expand Down Expand Up @@ -74,10 +70,9 @@ describe('Archiver', () => {

blocks = blockNumbers.map(x => L2Block.random(x, 4, x, x + 1, 2, 2));

const mockRollupRead = mock<MockRollupContractRead>({
((archiver as any).rollup as any).read = mock<MockRollupContractRead>({
archiveAt: (args: readonly [bigint]) => Promise.resolve(blocks[Number(args[0] - 1n)].archive.root.toString()),
});
(archiver as any).rollup = new MockRollupContract(mockRollupRead, rollupAddress.toString());
});

afterEach(async () => {
Expand Down
20 changes: 15 additions & 5 deletions yarn-project/archiver/src/archiver/archiver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { Fr } from '@aztec/foundation/fields';
import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log';
import { RunningPromise } from '@aztec/foundation/running-promise';
import { Timer } from '@aztec/foundation/timer';
import { RollupAbi } from '@aztec/l1-artifacts';
import { InboxAbi, RollupAbi } from '@aztec/l1-artifacts';
import { ClassRegistererAddress } from '@aztec/protocol-contracts/class-registerer';
import { type TelemetryClient } from '@aztec/telemetry-client';
import {
Expand All @@ -55,8 +55,12 @@ import {

import { type ArchiverDataStore } from './archiver_store.js';
import { type ArchiverConfig } from './config.js';
import { retrieveBlockFromRollup, retrieveL1ToL2Messages, retrieveL2ProofVerifiedEvents } from './data_retrieval.js';
import { getL1BlockTime } from './eth_log_handlers.js';
import {
getL1BlockTime,
retrieveBlockFromRollup,
retrieveL1ToL2Messages,
retrieveL2ProofVerifiedEvents,
} from './data_retrieval.js';
import { ArchiverInstrumentation } from './instrumentation.js';
import { type SingletonDataRetrieval } from './structs/data_retrieval.js';

Expand All @@ -77,6 +81,7 @@ export class Archiver implements ArchiveSource {
private runningPromise?: RunningPromise;

private rollup: GetContractReturnType<typeof RollupAbi, PublicClient<HttpTransport, Chain>>;
private inbox: GetContractReturnType<typeof InboxAbi, PublicClient<HttpTransport, Chain>>;

/**
* Creates a new instance of the Archiver.
Expand Down Expand Up @@ -104,6 +109,12 @@ export class Archiver implements ArchiveSource {
abi: RollupAbi,
client: publicClient,
});

this.inbox = getContract({
address: inboxAddress.toString(),
abi: InboxAbi,
client: publicClient,
});
}

/**
Expand Down Expand Up @@ -244,8 +255,7 @@ export class Archiver implements ArchiveSource {
// ********** Events that are processed per L2 block **********

const retrievedL1ToL2Messages = await retrieveL1ToL2Messages(
this.publicClient,
this.inboxAddress,
this.inbox,
blockUntilSynced,
messagesSynchedTo + 1n,
currentL1BlockNumber,
Expand Down
198 changes: 176 additions & 22 deletions yarn-project/archiver/src/archiver/data_retrieval.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,25 @@
import { type InboxLeaf, type L2Block } from '@aztec/circuit-types';
import { Fr, type Proof } from '@aztec/circuits.js';
import { EthAddress } from '@aztec/foundation/eth-address';
import { Body, InboxLeaf, L2Block } from '@aztec/circuit-types';
import { AppendOnlyTreeSnapshot, Fr, Header, Proof } from '@aztec/circuits.js';
import { type EthAddress } from '@aztec/foundation/eth-address';
import { type ViemSignature } from '@aztec/foundation/eth-signature';
import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log';
import { RollupAbi } from '@aztec/l1-artifacts';
import { numToUInt32BE } from '@aztec/foundation/serialize';
import { type InboxAbi, RollupAbi } from '@aztec/l1-artifacts';

import {
type Chain,
type GetContractEventsReturnType,
type GetContractReturnType,
type Hex,
type HttpTransport,
type PublicClient,
decodeFunctionData,
getAbiItem,
hexToBytes,
} from 'viem';

import {
getBlockProofFromSubmitProofTx,
getL2BlockProposedLogs,
getMessageSentLogs,
processL2BlockProposedLogs,
processMessageSentLogs,
} from './eth_log_handlers.js';
import { type DataRetrieval } from './structs/data_retrieval.js';
import { type L1Published } from './structs/published.js';
import { type L1Published, type L1PublishedData } from './structs/published.js';

/**
* Fetches new L2 blocks.
Expand All @@ -46,11 +44,12 @@ export async function retrieveBlockFromRollup(
if (searchStartBlock > searchEndBlock) {
break;
}
const l2BlockProposedLogs = await getL2BlockProposedLogs(
publicClient,
EthAddress.fromString(rollup.address),
searchStartBlock,
searchEndBlock,
const l2BlockProposedLogs = await rollup.getEvents.L2BlockProposed(
{},
{
fromBlock: searchStartBlock,
toBlock: searchEndBlock + 1n,
},
);

if (l2BlockProposedLogs.length === 0) {
Expand All @@ -69,6 +68,96 @@ export async function retrieveBlockFromRollup(
return retrievedBlocks;
}

/**
* Processes newly received L2BlockProposed logs.
* @param rollup - The rollup contract
* @param publicClient - The viem public client to use for transaction retrieval.
* @param logs - L2BlockProposed logs.
* @returns - An array blocks.
*/
export async function processL2BlockProposedLogs(
rollup: GetContractReturnType<typeof RollupAbi, PublicClient<HttpTransport, Chain>>,
publicClient: PublicClient,
logs: GetContractEventsReturnType<typeof RollupAbi, 'L2BlockProposed'>,
logger: DebugLogger,
): Promise<L1Published<L2Block>[]> {
const retrievedBlocks: L1Published<L2Block>[] = [];
for (const log of logs) {
const blockNum = log.args.blockNumber!;
const archive = log.args.archive!;
const archiveFromChain = await rollup.read.archiveAt([blockNum]);

// The value from the event and contract will match only if the block is in the chain.
if (archive === archiveFromChain) {
// TODO: Fetch blocks from calldata in parallel
const block = await getBlockFromRollupTx(publicClient, log.transactionHash!, blockNum);

const l1: L1PublishedData = {
blockNumber: log.blockNumber,
blockHash: log.blockHash,
timestamp: await getL1BlockTime(publicClient, log.blockNumber),
};

retrievedBlocks.push({ data: block, l1 });
} else {
logger.warn(
`Archive mismatch matching, ignoring block ${blockNum} with archive: ${archive}, expected ${archiveFromChain}`,
);
}
}

return retrievedBlocks;
}

export async function getL1BlockTime(publicClient: PublicClient, blockNumber: bigint): Promise<bigint> {
const block = await publicClient.getBlock({ blockNumber, includeTransactions: false });
return block.timestamp;
}

/**
* Gets block from the calldata of an L1 transaction.
* Assumes that the block was published from an EOA.
* TODO: Add retries and error management.
* @param publicClient - The viem public client to use for transaction retrieval.
* @param txHash - Hash of the tx that published it.
* @param l2BlockNum - L2 block number.
* @returns L2 block from the calldata, deserialized
*/
async function getBlockFromRollupTx(
publicClient: PublicClient,
txHash: `0x${string}`,
l2BlockNum: bigint,
): Promise<L2Block> {
const { input: data } = await publicClient.getTransaction({ hash: txHash });
const { functionName, args } = decodeFunctionData({
abi: RollupAbi,
data,
});

if (!(functionName === 'propose')) {
throw new Error(`Unexpected method called ${functionName}`);
}
const [headerHex, archiveRootHex, , , , bodyHex] = args! as readonly [Hex, Hex, Hex, Hex[], ViemSignature[], Hex];

const header = Header.fromBuffer(Buffer.from(hexToBytes(headerHex)));
const blockBody = Body.fromBuffer(Buffer.from(hexToBytes(bodyHex)));

const blockNumberFromHeader = header.globalVariables.blockNumber.toBigInt();

if (blockNumberFromHeader !== l2BlockNum) {
throw new Error(`Block number mismatch: expected ${l2BlockNum} but got ${blockNumberFromHeader}`);
}

const archive = AppendOnlyTreeSnapshot.fromBuffer(
Buffer.concat([
Buffer.from(hexToBytes(archiveRootHex)), // L2Block.archive.root
numToUInt32BE(Number(l2BlockNum + 1n)), // L2Block.archive.nextAvailableLeafIndex
]),
);

return new L2Block(archive, header, blockBody);
}

/**
* Fetch L1 to L2 messages.
* @param publicClient - The viem public client to use for transaction retrieval.
Expand All @@ -79,8 +168,7 @@ export async function retrieveBlockFromRollup(
* @returns An array of InboxLeaf and next eth block to search from.
*/
export async function retrieveL1ToL2Messages(
publicClient: PublicClient,
inboxAddress: EthAddress,
inbox: GetContractReturnType<typeof InboxAbi, PublicClient<HttpTransport, Chain>>,
blockUntilSynced: boolean,
searchStartBlock: bigint,
searchEndBlock: bigint,
Expand All @@ -90,12 +178,24 @@ export async function retrieveL1ToL2Messages(
if (searchStartBlock > searchEndBlock) {
break;
}
const messageSentLogs = await getMessageSentLogs(publicClient, inboxAddress, searchStartBlock, searchEndBlock);

const messageSentLogs = await inbox.getEvents.MessageSent(
{},
{
fromBlock: searchStartBlock,
toBlock: searchEndBlock + 1n,
},
);

if (messageSentLogs.length === 0) {
break;
}
const l1ToL2Messages = processMessageSentLogs(messageSentLogs);
retrievedL1ToL2Messages.push(...l1ToL2Messages);

for (const log of messageSentLogs) {
const { l2BlockNumber, index, hash } = log.args;
retrievedL1ToL2Messages.push(new InboxLeaf(l2BlockNumber!, index!, Fr.fromString(hash!)));
}

// handles the case when there are no new messages:
searchStartBlock = (messageSentLogs.findLast(msgLog => !!msgLog)?.blockNumber || searchStartBlock) + 1n;
} while (blockUntilSynced && searchStartBlock <= searchEndBlock);
Expand Down Expand Up @@ -145,3 +245,57 @@ export async function retrieveL2ProofsFromRollup(
lastProcessedL1BlockNumber,
};
}

export type SubmitBlockProof = {
header: Header;
archiveRoot: Fr;
proverId: Fr;
aggregationObject: Buffer;
proof: Proof;
};

/**
* Gets block metadata (header and archive snapshot) from the calldata of an L1 transaction.
* Assumes that the block was published from an EOA.
* TODO: Add retries and error management.
* @param publicClient - The viem public client to use for transaction retrieval.
* @param txHash - Hash of the tx that published it.
* @param l2BlockNum - L2 block number.
* @returns L2 block metadata (header and archive) from the calldata, deserialized
*/
export async function getBlockProofFromSubmitProofTx(
publicClient: PublicClient,
txHash: `0x${string}`,
l2BlockNum: bigint,
expectedProverId: Fr,
): Promise<SubmitBlockProof> {
const { input: data } = await publicClient.getTransaction({ hash: txHash });
const { functionName, args } = decodeFunctionData({
abi: RollupAbi,
data,
});

if (!(functionName === 'submitBlockRootProof')) {
throw new Error(`Unexpected method called ${functionName}`);
}
const [headerHex, archiveHex, proverIdHex, aggregationObjectHex, proofHex] = args!;

const header = Header.fromBuffer(Buffer.from(hexToBytes(headerHex)));
const proverId = Fr.fromString(proverIdHex);

const blockNumberFromHeader = header.globalVariables.blockNumber.toBigInt();
if (blockNumberFromHeader !== l2BlockNum) {
throw new Error(`Block number mismatch: expected ${l2BlockNum} but got ${blockNumberFromHeader}`);
}
if (!proverId.equals(expectedProverId)) {
throw new Error(`Prover ID mismatch: expected ${expectedProverId} but got ${proverId}`);
}

return {
header,
proverId,
aggregationObject: Buffer.from(hexToBytes(aggregationObjectHex)),
archiveRoot: Fr.fromString(archiveHex),
proof: Proof.fromBuffer(Buffer.from(hexToBytes(proofHex))),
};
}
Loading

0 comments on commit 1e4b1f9

Please sign in to comment.