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
40 changes: 17 additions & 23 deletions yarn-project/archiver/src/l1/bin/retrieve-calldata.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
#!/usr/bin/env node
import type { ViemPublicClient, ViemPublicDebugClient } from '@aztec/ethereum/types';
import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
import { CheckpointNumber } from '@aztec/foundation/branded-types';
import { EthAddress } from '@aztec/foundation/eth-address';
import { createLogger } from '@aztec/foundation/log';
import { RollupAbi } from '@aztec/l1-artifacts/RollupAbi';

import { type Hex, createPublicClient, http } from 'viem';
import { type Hex, createPublicClient, getAbiItem, http, toEventSelector } from 'viem';
import { mainnet } from 'viem/chains';

import { CalldataRetriever } from '../calldata_retriever.js';
Expand Down Expand Up @@ -111,43 +112,36 @@ async function main() {
},
);

// Extract L2 block number from transaction logs
logger.info('Decoding transaction to extract L2 block number...');
// Extract checkpoint number from transaction logs
logger.info('Decoding transaction to extract checkpoint number...');
const receipt = await publicClient.getTransactionReceipt({ hash: txHash });
const l2BlockProposedEvent = receipt.logs.find(log => {

// Look for CheckpointProposed event (emitted when a checkpoint is proposed to the rollup)
// Event signature: CheckpointProposed(uint256 indexed checkpointNumber, bytes32 indexed archive, bytes32[], bytes32, bytes32)
// Hash: keccak256("CheckpointProposed(uint256,bytes32,bytes32[],bytes32,bytes32)")
const checkpointProposedEvent = receipt.logs.find(log => {
try {
// Try to match the L2BlockProposed event
return (
log.address.toLowerCase() === rollupAddress.toString().toLowerCase() &&
log.topics[0] === '0x2f1d0e696fa5186494a2f2f89a0e0bcbb15d607f6c5eac4637e07e1e5e7d3c00' // L2BlockProposed event signature
log.topics[0] === toEventSelector(getAbiItem({ abi: RollupAbi, name: 'CheckpointProposed' }))
);
} catch {
return false;
}
});

let l2BlockNumber: number;
if (l2BlockProposedEvent && l2BlockProposedEvent.topics[1]) {
// L2 block number is typically the first indexed parameter
l2BlockNumber = Number(BigInt(l2BlockProposedEvent.topics[1]));
logger.info(`L2 Block Number (from event): ${l2BlockNumber}`);
} else {
// Fallback: try to extract from transaction data or use a default
logger.warn('Could not extract L2 block number from event, using block number as fallback');
l2BlockNumber = Number(tx.blockNumber);
if (!checkpointProposedEvent || checkpointProposedEvent.topics[1] === undefined) {
throw new Error(`Checkpoint proposed event not found`);
}

const checkpointNumber = CheckpointNumber.fromBigInt(BigInt(checkpointProposedEvent.topics[1]));

logger.info('');
logger.info('Retrieving block header from rollup transaction...');
logger.info('Retrieving checkpoint from rollup transaction...');
logger.info('');

// For this script, we don't have blob hashes or expected hashes, so pass empty arrays/objects
const result = await retriever.getCheckpointFromRollupTx(
txHash,
[],
CheckpointNumber.fromBlockNumber(BlockNumber(l2BlockNumber)),
{},
);
const result = await retriever.getCheckpointFromRollupTx(txHash, [], checkpointNumber, {});

logger.info(' Successfully retrieved block header!');
logger.info('');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { createAztecNodeClient, waitForNode } from '@aztec/aztec.js/node';
import { createExtendedL1Client } from '@aztec/ethereum/client';
import { RollupContract } from '@aztec/ethereum/contracts';
import { deployL1Contract } from '@aztec/ethereum/deploy-l1-contract';
import { CheckpointNumber } from '@aztec/foundation/branded-types';
import {
FeeAssetHandlerAbi,
FeeAssetHandlerBytecode,
Expand Down Expand Up @@ -218,7 +217,8 @@ describe('e2e_cross_chain_messaging token_bridge_tutorial_test', () => {

// docs:start:l1-withdraw
const rollup = new RollupContract(l1Client, l1ContractAddresses.rollupAddress.toString());
const epoch = await rollup.getEpochNumberForCheckpoint(CheckpointNumber.fromBlockNumber(l2TxReceipt.blockNumber!));
const block = await node.getBlock(l2TxReceipt.blockNumber!);
const epoch = await rollup.getEpochNumberForCheckpoint(block!.checkpointNumber);

const result = await computeL2ToL1MembershipWitness(node, epoch, l2ToL1Message);
if (!result) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import type {
} from '@aztec/ethereum/deploy-aztec-l1-contracts';
import { deployL1Contract } from '@aztec/ethereum/deploy-l1-contract';
import type { ExtendedViemWalletClient } from '@aztec/ethereum/types';
import { CheckpointNumber, EpochNumber } from '@aztec/foundation/branded-types';
import { EpochNumber } from '@aztec/foundation/branded-types';
import { sleep } from '@aztec/foundation/sleep';
import { TestERC20Abi, TestERC20Bytecode } from '@aztec/l1-artifacts';
import { TokenContract } from '@aztec/noir-contracts.js/Token';
Expand Down Expand Up @@ -86,9 +86,8 @@ export class CrossChainMessagingTest {
}

async advanceToEpochProven(l2TxReceipt: TxReceipt): Promise<EpochNumber> {
const epoch = await this.rollup.getEpochNumberForCheckpoint(
CheckpointNumber.fromBlockNumber(l2TxReceipt.blockNumber!),
);
const block = await this.aztecNode.getBlock(l2TxReceipt.blockNumber!);
const epoch = await this.rollup.getEpochNumberForCheckpoint(block!.checkpointNumber);
// Warp to the next epoch.
await this.cheatCodes.rollup.advanceToEpoch(EpochNumber(epoch + 1));
// Wait for the tx to be proven.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { RollupContract } from '@aztec/ethereum/contracts';
import type { Operator } from '@aztec/ethereum/deploy-aztec-l1-contracts';
import type { ExtendedViemWalletClient } from '@aztec/ethereum/types';
import { asyncMap } from '@aztec/foundation/async-map';
import { CheckpointNumber, SlotNumber } from '@aztec/foundation/branded-types';
import { BlockNumber, CheckpointNumber, SlotNumber } from '@aztec/foundation/branded-types';
import { times } from '@aztec/foundation/collection';
import { SecretValue } from '@aztec/foundation/config';
import { EthAddress } from '@aztec/foundation/eth-address';
Expand Down Expand Up @@ -455,7 +455,8 @@ describe('e2e_epochs/epochs_invalidate_block', () => {
const [event] = checkpointInvalidatedEvents;
logger.warn(`CheckpointInvalidated event emitted`, { event });
expect(event.args.checkpointNumber).toBeGreaterThan(initialBlockNumber);
expect(await test.rollup.getCheckpointNumber()).toEqual(CheckpointNumber.fromBlockNumber(initialBlockNumber));
const initialCheckpointNumber = await getCheckpointNumberForBlock(nodes[0], initialBlockNumber);
expect(await test.rollup.getCheckpointNumber()).toEqual(initialCheckpointNumber);

logger.warn(`Test succeeded '${expect.getState().currentTestName}'`);
});
Expand Down Expand Up @@ -511,7 +512,8 @@ describe('e2e_epochs/epochs_invalidate_block', () => {
const [event] = checkpointInvalidatedEvents;
logger.warn(`CheckpointInvalidated event emitted`, { event });
expect(event.args.checkpointNumber).toBeGreaterThan(initialBlockNumber);
expect(await test.rollup.getCheckpointNumber()).toEqual(CheckpointNumber.fromBlockNumber(initialBlockNumber));
const initialCheckpointNumber = await getCheckpointNumberForBlock(nodes[0], initialBlockNumber);
expect(await test.rollup.getCheckpointNumber()).toEqual(initialCheckpointNumber);

logger.warn(`Test succeeded '${expect.getState().currentTestName}'`);
});
Expand Down Expand Up @@ -586,3 +588,17 @@ describe('e2e_epochs/epochs_invalidate_block', () => {
logger.warn(`Test succeeded '${expect.getState().currentTestName}'`);
});
});

async function getCheckpointNumberForBlock(
node: AztecNodeService,
blockNumber: BlockNumber,
): Promise<CheckpointNumber> {
if (blockNumber === 0) {
return CheckpointNumber(0);
}
const block = await node.getBlock(blockNumber);
if (!block) {
throw new Error(`Block ${blockNumber} not found`);
}
return block.checkpointNumber;
}
10 changes: 6 additions & 4 deletions yarn-project/end-to-end/src/e2e_epochs/epochs_multiple.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { EpochsTestContext, WORLD_STATE_BLOCK_HISTORY } from './epochs_test.js';

jest.setTimeout(1000 * 60 * 15);

// Assumes one block per checkpoint
describe('e2e_epochs/epochs_multiple', () => {
let context: EndToEndContext;
let rollup: RollupContract;
Expand Down Expand Up @@ -42,13 +43,14 @@ describe('e2e_epochs/epochs_multiple', () => {
logger.info(`Reached proven checkpoint number ${epochEndCheckpointNumber}, epoch ${epochNumber} is now proven`);
epochNumber++;

// Verify the state syncs
await test.waitForNodeToSync(BlockNumber.fromCheckpointNumber(epochEndCheckpointNumber), 'proven');
await test.verifyHistoricBlock(BlockNumber.fromCheckpointNumber(epochEndCheckpointNumber), true);
// Verify the state syncs. Assumes one block per checkpoint.
const epochEndBlockNumber = BlockNumber.fromCheckpointNumber(epochEndCheckpointNumber);
await test.waitForNodeToSync(epochEndBlockNumber, 'proven');
await test.verifyHistoricBlock(epochEndBlockNumber, true);

// Check that finalized blocks are purged from world state
// Right now finalization means a checkpoint is two L2 epochs deep. If this rule changes then this test needs to be updated.
const provenBlockNumber = BlockNumber.fromCheckpointNumber(epochEndCheckpointNumber);
const provenBlockNumber = epochEndBlockNumber;
const finalizedBlockNumber = Math.max(provenBlockNumber - context.config.aztecEpochDuration * 2, 0);
const expectedOldestHistoricBlock = Math.max(finalizedBlockNumber - WORLD_STATE_BLOCK_HISTORY + 1, 1);
const expectedBlockRemoved = expectedOldestHistoricBlock - 1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ describe('L1Publisher integration', () => {
slicedBlocks.map(
async block =>
new CheckpointedL2Block(
// Test uses 1-block-per-checkpoint, so checkpoint number equals block number
CheckpointNumber.fromBlockNumber(block.number),
block,
new L1PublishedData(BigInt(block.number), BigInt(block.number), (await block.hash()).toString()),
Expand All @@ -205,6 +206,7 @@ describe('L1Publisher integration', () => {
);
},
async getCheckpoints(checkpointNumber, _limit) {
// Test uses 1-block-per-checkpoint, so we find block by checkpoint number
const block = blocks.find(b => Number(b.number) === Number(checkpointNumber));
if (!block) {
return Promise.resolve([]);
Expand All @@ -213,7 +215,7 @@ describe('L1Publisher integration', () => {
block.archive,
CheckpointHeader.random({ lastArchiveRoot: block.header.lastArchive.root }),
[block],
CheckpointNumber.fromBlockNumber(block.number),
checkpointNumber,
);
return [
new PublishedCheckpoint(
Expand All @@ -228,6 +230,7 @@ describe('L1Publisher integration', () => {
const blockId = latestBlock
? { number: latestBlock.number, hash: (await latestBlock.hash()).toString() }
: { number: BlockNumber.ZERO, hash: GENESIS_BLOCK_HEADER_HASH.toString() };
// Test uses 1-block-per-checkpoint, so checkpoint number equals block number
const tipId = {
block: blockId,
checkpoint: { number: CheckpointNumber.fromBlockNumber(blockId.number), hash: blockId.hash },
Expand Down Expand Up @@ -352,6 +355,7 @@ describe('L1Publisher integration', () => {
gasFees: globalVariables.gasFees,
};

// Test uses 1-block-per-checkpoint
const checkpointNumber = CheckpointNumber.fromBlockNumber(globalVariables.blockNumber);
const builder = await LightweightCheckpointBuilder.startNewCheckpoint(
checkpointNumber,
Expand Down Expand Up @@ -491,7 +495,7 @@ describe('L1Publisher integration', () => {
});
expect(logs).toHaveLength(i + 1);
expect(logs[i].args.checkpointNumber).toEqual(BigInt(i + 1));
const thisCheckpointNumber = CheckpointNumber.fromBlockNumber(block.header.globalVariables.blockNumber);
const thisCheckpointNumber = checkpoint.number;
const prevCheckpointNumber = CheckpointNumber(thisCheckpointNumber - 1);
const isFirstCheckpointOfEpoch =
thisCheckpointNumber == CheckpointNumber(1) ||
Expand Down Expand Up @@ -895,7 +899,6 @@ describe('L1Publisher integration', () => {

it(`speeds up block proposal if not mined`, async () => {
const { checkpoint } = await buildSingleCheckpoint();
const block = checkpoint.blocks[0];
await enqueueProposeL2Checkpoint(checkpoint);
await sendRequests();

Expand Down Expand Up @@ -925,7 +928,7 @@ describe('L1Publisher integration', () => {
expect(minedTx).toBeDefined();
const minedTxReceipt = await l1Client.getTransactionReceipt({ hash: minedTx!.hash });
expect(minedTxReceipt.status).toEqual('success');
expect(await rollup.getCheckpointNumber()).toEqual(CheckpointNumber.fromBlockNumber(block.number));
expect(await rollup.getCheckpointNumber()).toEqual(checkpoint.number);
});

it(`can send two consecutive proposals if the first one times out`, async () => {
Expand Down Expand Up @@ -978,8 +981,8 @@ describe('L1Publisher integration', () => {
expect(sendRequestsResult).not.toBeNull();
expect(sendRequestsResult!.successfulActions).toEqual(['propose']);
expect(sendRequestsResult!.failedActions).toEqual([]);
expect(await rollup.getCheckpointNumber()).toEqual(CheckpointNumber.fromBlockNumber(block2.number));
const rollupBlock = await rollup.getCheckpoint(CheckpointNumber.fromBlockNumber(block2.number));
expect(await rollup.getCheckpointNumber()).toEqual(checkpoint2.number);
const rollupBlock = await rollup.getCheckpoint(checkpoint2.number);
expect(rollupBlock.slotNumber).toEqual(block2.slot);
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { createExtendedL1Client } from '@aztec/ethereum/client';
import { getL1ContractsConfigEnvVars } from '@aztec/ethereum/config';
import { RollupContract } from '@aztec/ethereum/contracts';
import type { DeployAztecL1ContractsReturnType } from '@aztec/ethereum/deploy-aztec-l1-contracts';
import { CheckpointNumber, EpochNumber } from '@aztec/foundation/branded-types';
import { EpochNumber } from '@aztec/foundation/branded-types';
import { SecretValue } from '@aztec/foundation/config';
import { Signature } from '@aztec/foundation/eth-signature';
import { retryUntil } from '@aztec/foundation/retry';
Expand Down Expand Up @@ -127,7 +127,8 @@ describe('e2e_multi_validator_node', () => {
expect(tx.blockNumber).toBeDefined();

const dataStore = (aztecNode as AztecNodeService).getBlockSource() as Archiver;
const [publishedCheckpoint] = await dataStore.getCheckpoints(CheckpointNumber.fromBlockNumber(tx.blockNumber!), 1);
const checkpointedBlock = await dataStore.getCheckpointedBlock(tx.blockNumber!);
const [publishedCheckpoint] = await dataStore.getCheckpoints(checkpointedBlock!.checkpointNumber, 1);
const payload = ConsensusPayload.fromCheckpoint(publishedCheckpoint.checkpoint);
const attestations = publishedCheckpoint.attestations
.filter(a => !a.signature.isEmpty())
Expand All @@ -139,6 +140,7 @@ describe('e2e_multi_validator_node', () => {

expect(signers.every(s => validatorAddresses.includes(s))).toBe(true);
});

it('should attest ONLY with the correct validator keys', async () => {
const rollupContract1 = getContract({
address: deployL1ContractsValues.l1ContractAddresses.rollupAddress.toString(),
Expand Down Expand Up @@ -188,7 +190,8 @@ describe('e2e_multi_validator_node', () => {
expect(tx.blockNumber).toBeDefined();

const dataStore = (aztecNode as AztecNodeService).getBlockSource() as Archiver;
const [publishedCheckpoint] = await dataStore.getCheckpoints(CheckpointNumber.fromBlockNumber(tx.blockNumber!), 1);
const checkpointedBlock = await dataStore.getCheckpointedBlock(tx.blockNumber!);
const [publishedCheckpoint] = await dataStore.getCheckpoints(checkpointedBlock!.checkpointNumber, 1);
const payload = ConsensusPayload.fromCheckpoint(publishedCheckpoint.checkpoint);
const attestations = publishedCheckpoint.attestations
.filter(a => !a.signature.isEmpty())
Expand Down
5 changes: 2 additions & 3 deletions yarn-project/end-to-end/src/e2e_p2p/add_rollup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -350,9 +350,8 @@ describe('e2e_p2p_add_rollup', () => {
});

const rollup = new RollupContract(l1Client, l1ContractAddresses.rollupAddress);
const epoch = await rollup.getEpochNumberForCheckpoint(
CheckpointNumber.fromBlockNumber(l2OutgoingReceipt.blockNumber!),
);
const block = await node.getBlock(l2OutgoingReceipt.blockNumber!);
const epoch = await rollup.getEpochNumberForCheckpoint(block!.checkpointNumber);

const l2ToL1MessageResult = (await computeL2ToL1MembershipWitness(node, epoch, leaf))!;
const leafId = getL2ToL1MessageLeafId(l2ToL1MessageResult);
Expand Down
4 changes: 2 additions & 2 deletions yarn-project/end-to-end/src/e2e_p2p/gossip_network.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import type { Archiver } from '@aztec/archiver';
import type { AztecNodeConfig, AztecNodeService } from '@aztec/aztec-node';
import { waitForTx } from '@aztec/aztec.js/node';
import { TxHash } from '@aztec/aztec.js/tx';
import { CheckpointNumber } from '@aztec/foundation/branded-types';
import { Signature } from '@aztec/foundation/eth-signature';
import { retryUntil } from '@aztec/foundation/retry';
import type { ProverNode } from '@aztec/prover-node';
Expand Down Expand Up @@ -189,7 +188,8 @@ describe('e2e_p2p_network', () => {
const receipt = await nodes[0].getTxReceipt(txsSentViaDifferentNodes[0][0]);
const blockNumber = receipt.blockNumber!;
const dataStore = (nodes[0] as AztecNodeService).getBlockSource() as Archiver;
const [publishedCheckpoint] = await dataStore.getCheckpoints(CheckpointNumber.fromBlockNumber(blockNumber), 1);
const checkpointedBlock = await dataStore.getCheckpointedBlock(blockNumber);
const [publishedCheckpoint] = await dataStore.getCheckpoints(checkpointedBlock!.checkpointNumber, 1);
const payload = ConsensusPayload.fromCheckpoint(publishedCheckpoint.checkpoint);
const attestations = publishedCheckpoint.attestations
.filter(a => !a.signature.isEmpty())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { waitForTx } from '@aztec/aztec.js/node';
import { TxHash } from '@aztec/aztec.js/tx';
import { addL1Validator } from '@aztec/cli/l1/validators';
import { RollupContract } from '@aztec/ethereum/contracts';
import { CheckpointNumber, EpochNumber } from '@aztec/foundation/branded-types';
import { EpochNumber } from '@aztec/foundation/branded-types';
import { Signature } from '@aztec/foundation/eth-signature';
import { sleep } from '@aztec/foundation/sleep';
import { MockZKPassportVerifierAbi } from '@aztec/l1-artifacts/MockZKPassportVerifierAbi';
Expand Down Expand Up @@ -224,7 +224,8 @@ describe('e2e_p2p_network', () => {
// Gather signers from attestations downloaded from L1
const blockNumber = await nodes[0].getTxReceipt(txsSentViaDifferentNodes[0][0]).then(r => r.blockNumber!);
const dataStore = (nodes[0] as AztecNodeService).getBlockSource() as Archiver;
const [publishedCheckpoint] = await dataStore.getCheckpoints(CheckpointNumber.fromBlockNumber(blockNumber), 1);
const checkpointedBlock = await dataStore.getCheckpointedBlock(blockNumber);
const [publishedCheckpoint] = await dataStore.getCheckpoints(checkpointedBlock!.checkpointNumber, 1);
const payload = ConsensusPayload.fromCheckpoint(publishedCheckpoint.checkpoint);
const attestations = publishedCheckpoint.attestations
.filter(a => !a.signature.isEmpty())
Expand Down
Loading
Loading