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
2 changes: 1 addition & 1 deletion .github/workflows/deploy-staging-public.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:

if [ -n "$TAG" ]; then
echo "Found tag: $TAG"
SEMVER="${VERSION}"
SEMVER="${TAG#v}"
echo "tag=$TAG" >> $GITHUB_OUTPUT
echo "semver=$SEMVER" >> $GITHUB_OUTPUT
exit 0
Expand Down
11 changes: 6 additions & 5 deletions aztec-up/bin/0.0.1/aztec-install
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,12 @@ function title {
echo -e "Installing version: ${bold}${g}$VERSION${r}"
echo
echo -e "This install script will install the following and update your PATH if necessary:"
echo -e " ${bold}${g}nargo${r} - the version of the noir programming language compatible with this version of aztec."
echo -e " ${bold}${g}bb${r} - the version of the barretenberg proving backend compatible with this version of aztec."
echo -e " ${bold}${g}aztec${r} - a collection of tools to compile and test contracts, to launch subsystems and interact with the aztec network."
echo -e " ${bold}${g}aztec-up${r} - a tool to install and manage aztec toolchain versions."
echo -e " ${bold}${g}aztec-wallet${r} - our minimalistic CLI wallet"
echo -e " ${bold}${g}nargo${r} - the version of the noir programming language compatible with this version of aztec."
echo -e " ${bold}${g}noir-profiler${r} - a sampling profiler for analyzing and visualizing Noir programs."
echo -e " ${bold}${g}bb${r} - the version of the barretenberg proving backend compatible with this version of aztec."
echo -e " ${bold}${g}aztec${r} - a collection of tools to compile and test contracts, to launch subsystems and interact with the aztec network."
echo -e " ${bold}${g}aztec-up${r} - a tool to install and manage aztec toolchain versions."
echo -e " ${bold}${g}aztec-wallet${r} - our minimalistic CLI wallet"
echo
read -p "Do you wish to continue? (y/n) " -n 1 -r
echo
Expand Down
2 changes: 2 additions & 0 deletions aztec-up/bin/0.0.1/install
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ function install_noir {

# Move the nargo binary to our version bin directory
mv "$temp_nargo_home/bin/nargo" "$version_bin_path/nargo"
# Also move noir-profiler, needed by `aztec profile flamegraph`
mv "$temp_nargo_home/bin/noir-profiler" "$version_bin_path/noir-profiler"

# Cleanup temp directory
rm -rf "$temp_nargo_home"
Expand Down
17 changes: 11 additions & 6 deletions barretenberg/cpp/src/barretenberg/wasi/wasi_stubs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,22 @@ int32_t __imported_wasi_snapshot_preview1_fd_close(int32_t)
return 0;
}

int32_t __imported_wasi_snapshot_preview1_environ_get(int32_t, int32_t)
int32_t __imported_wasi_snapshot_preview1_environ_get(int32_t environ_ptr, int32_t environ_buf_ptr)
{
// info("environ_get not implemented.");
// abort();
// No environment variables, so nothing to write. The pointers point to
// arrays that would hold the environ entries and the concatenated
// key=value strings respectively, but with count == 0 they are empty.
(void)environ_ptr;
(void)environ_buf_ptr;
return 0;
}

int32_t __imported_wasi_snapshot_preview1_environ_sizes_get(int32_t, int32_t)
int32_t __imported_wasi_snapshot_preview1_environ_sizes_get(int32_t count_ptr, int32_t buf_size_ptr)
{
// info("environ_sizes_get not implemented.");
// abort();
// WASI requires writing the number of environment variables and the total
// buffer size needed to hold them. We have none of either.
*(int32_t*)(uintptr_t)count_ptr = 0;
*(int32_t*)(uintptr_t)buf_size_ptr = 0;
return 0;
}

Expand Down
112 changes: 112 additions & 0 deletions yarn-project/archiver/src/archiver-store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -432,4 +432,116 @@ describe('Archiver Store', () => {
expect(result).toEqual([]);
});
});

describe('rollbackTo', () => {
beforeEach(() => {
publicClient.getBlock.mockImplementation(
(args: { blockNumber?: bigint } = {}) =>
Promise.resolve({ number: args.blockNumber ?? 0n, hash: `0x${'0'.repeat(64)}` }) as any,
);
});

it('rejects rollback to a block that is not at a checkpoint boundary', async () => {
const genesisArchive = new AppendOnlyTreeSnapshot(new Fr(GENESIS_ARCHIVE_ROOT), 1);
// Checkpoint 1: 3 blocks (1, 2, 3). Checkpoint 2: 3 blocks (4, 5, 6).
const testCheckpoints = await makeChainedCheckpoints(2, {
previousArchive: genesisArchive,
blocksPerCheckpoint: 3,
});
await archiverStore.addCheckpoints(testCheckpoints);

// Block 1 is not at a checkpoint boundary (checkpoint 1 ends at block 3)
await expect(archiver.rollbackTo(BlockNumber(1))).rejects.toThrow(
/not at a checkpoint boundary.*Use block 3 to roll back to this checkpoint.*or block 0 to roll back to the previous one/,
);

// Block 2 is also not at a checkpoint boundary
await expect(archiver.rollbackTo(BlockNumber(2))).rejects.toThrow(
/not at a checkpoint boundary.*Use block 3 to roll back to this checkpoint.*or block 0 to roll back to the previous one/,
);
});

it('allows rollback to the last block of a checkpoint and updates sync points', async () => {
const genesisArchive = new AppendOnlyTreeSnapshot(new Fr(GENESIS_ARCHIVE_ROOT), 1);
// Checkpoint 1: 3 blocks (1, 2, 3), L1 block 10. Checkpoint 2: 3 blocks (4, 5, 6), L1 block 20.
const testCheckpoints = await makeChainedCheckpoints(2, {
previousArchive: genesisArchive,
blocksPerCheckpoint: 3,
});
await archiverStore.addCheckpoints(testCheckpoints);

// Block 3 is the last block of checkpoint 1 — should succeed
await archiver.rollbackTo(BlockNumber(3));

expect(await archiver.getSynchedCheckpointNumber()).toEqual(CheckpointNumber(1));

// Verify sync points are set to checkpoint 1's L1 block number (10)
const synchPoint = await archiverStore.getSynchPoint();
expect(synchPoint.blocksSynchedTo).toEqual(10n);
expect(synchPoint.messagesSynchedTo?.l1BlockNumber).toEqual(10n);
});

it('includes correct boundary info in error for mid-checkpoint rollback', async () => {
const genesisArchive = new AppendOnlyTreeSnapshot(new Fr(GENESIS_ARCHIVE_ROOT), 1);
// Checkpoint 1: 2 blocks (1, 2). Checkpoint 2: 3 blocks (3, 4, 5).
const checkpoints1 = await makeChainedCheckpoints(1, {
previousArchive: genesisArchive,
blocksPerCheckpoint: 2,
});
const checkpoints2 = await makeChainedCheckpoints(1, {
previousArchive: checkpoints1[0].checkpoint.blocks.at(-1)!.archive,
startCheckpointNumber: CheckpointNumber(2),
startBlockNumber: 3,
startL1BlockNumber: 20,
blocksPerCheckpoint: 3,
});
await archiverStore.addCheckpoints([...checkpoints1, ...checkpoints2]);

// Block 3 is the first of checkpoint 2 (spans 3-5)
// Should suggest block 5 (end of this checkpoint) or block 2 (end of previous)
await expect(archiver.rollbackTo(BlockNumber(3))).rejects.toThrow(
/Checkpoint 2 spans blocks 3 to 5.*Use block 5 to roll back to this checkpoint.*or block 2 to roll back to the previous one/,
);
});

it('rolls back proven checkpoint number when target is before proven block', async () => {
const genesisArchive = new AppendOnlyTreeSnapshot(new Fr(GENESIS_ARCHIVE_ROOT), 1);
// Checkpoint 1: blocks 1-2, Checkpoint 2: blocks 3-4, Checkpoint 3: blocks 5-6
const testCheckpoints = await makeChainedCheckpoints(3, {
previousArchive: genesisArchive,
blocksPerCheckpoint: 2,
});
await archiverStore.addCheckpoints(testCheckpoints);

// Mark checkpoint 2 as proven
await archiverStore.setProvenCheckpointNumber(CheckpointNumber(2));
expect(await archiver.getProvenCheckpointNumber()).toEqual(CheckpointNumber(2));

// Roll back to block 2 (end of checkpoint 1), which is before proven block 4
await archiver.rollbackTo(BlockNumber(2));

expect(await archiver.getSynchedCheckpointNumber()).toEqual(CheckpointNumber(1));
expect(await archiver.getProvenCheckpointNumber()).toEqual(CheckpointNumber(1));
});

it('preserves proven checkpoint number when target is after proven block', async () => {
const genesisArchive = new AppendOnlyTreeSnapshot(new Fr(GENESIS_ARCHIVE_ROOT), 1);
// Checkpoint 1: blocks 1-2, Checkpoint 2: blocks 3-4, Checkpoint 3: blocks 5-6
const testCheckpoints = await makeChainedCheckpoints(3, {
previousArchive: genesisArchive,
blocksPerCheckpoint: 2,
});
await archiverStore.addCheckpoints(testCheckpoints);

// Mark checkpoint 1 as proven
await archiverStore.setProvenCheckpointNumber(CheckpointNumber(1));
expect(await archiver.getProvenCheckpointNumber()).toEqual(CheckpointNumber(1));

// Roll back to block 4 (end of checkpoint 2), which is after proven block 2
await archiver.rollbackTo(BlockNumber(4));

expect(await archiver.getSynchedCheckpointNumber()).toEqual(CheckpointNumber(2));
expect(await archiver.getProvenCheckpointNumber()).toEqual(CheckpointNumber(1));
});
});
});
29 changes: 23 additions & 6 deletions yarn-project/archiver/src/archiver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,6 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
}

public async rollbackTo(targetL2BlockNumber: BlockNumber): Promise<void> {
// TODO(pw/mbps): This still assumes 1 block per checkpoint
const currentBlocks = await this.getL2Tips();
const currentL2Block = currentBlocks.proposed.number;
const currentProvenBlock = currentBlocks.proven.block.number;
Expand All @@ -411,8 +410,25 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
if (!targetL2Block) {
throw new Error(`Target L2 block ${targetL2BlockNumber} not found`);
}
const targetL1BlockNumber = targetL2Block.l1.blockNumber;
const targetCheckpointNumber = targetL2Block.checkpointNumber;

// Rollback operates at checkpoint granularity: the target block must be the last block of its checkpoint.
const checkpointData = await this.store.getCheckpointData(targetCheckpointNumber);
if (checkpointData) {
const lastBlockInCheckpoint = BlockNumber(checkpointData.startBlock + checkpointData.blockCount - 1);
if (targetL2BlockNumber !== lastBlockInCheckpoint) {
const previousCheckpointBoundary =
checkpointData.startBlock > 1 ? BlockNumber(checkpointData.startBlock - 1) : BlockNumber(0);
throw new Error(
`Target L2 block ${targetL2BlockNumber} is not at a checkpoint boundary. ` +
`Checkpoint ${targetCheckpointNumber} spans blocks ${checkpointData.startBlock} to ${lastBlockInCheckpoint}. ` +
`Use block ${lastBlockInCheckpoint} to roll back to this checkpoint, ` +
`or block ${previousCheckpointBoundary} to roll back to the previous one.`,
);
}
}

const targetL1BlockNumber = targetL2Block.l1.blockNumber;
const targetL1Block = await this.publicClient.getBlock({
blockNumber: targetL1BlockNumber,
includeTransactions: false,
Expand All @@ -431,13 +447,14 @@ export class Archiver extends ArchiverDataSourceBase implements L2BlockSink, Tra
await this.store.setCheckpointSynchedL1BlockNumber(targetL1BlockNumber);
await this.store.setMessageSynchedL1Block({ l1BlockNumber: targetL1BlockNumber, l1BlockHash: targetL1BlockHash });
if (targetL2BlockNumber < currentProvenBlock) {
this.log.info(`Clearing proven L2 block number`);
await this.updater.setProvenCheckpointNumber(CheckpointNumber.ZERO);
this.log.info(`Rolling back proven L2 checkpoint to ${targetCheckpointNumber}`);
await this.updater.setProvenCheckpointNumber(targetCheckpointNumber);
}
// TODO(palla/reorg): Set the finalized block when we add support for it.
// const currentFinalizedBlock = currentBlocks.finalized.block.number;
// if (targetL2BlockNumber < currentFinalizedBlock) {
// this.log.info(`Clearing finalized L2 block number`);
// await this.store.setFinalizedL2BlockNumber(0);
// this.log.info(`Rolling back finalized L2 checkpoint to ${targetCheckpointNumber}`);
// await this.updater.setFinalizedCheckpointNumber(targetCheckpointNumber);
// }
}
}
2 changes: 1 addition & 1 deletion yarn-project/p2p/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export interface P2PConfig
ChainConfig,
TxCollectionConfig,
TxFileStoreConfig,
Pick<SequencerConfig, 'blockDurationMs' | 'expectedBlockProposalsPerSlot'> {
Pick<SequencerConfig, 'blockDurationMs' | 'expectedBlockProposalsPerSlot' | 'maxTxsPerBlock'> {
/** A flag dictating whether the P2P subsystem should be enabled. */
p2pEnabled: boolean;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { BlockProposal, P2PValidator } from '@aztec/stdlib/p2p';
import { ProposalValidator } from '../proposal_validator/proposal_validator.js';

export class BlockProposalValidator extends ProposalValidator<BlockProposal> implements P2PValidator<BlockProposal> {
constructor(epochCache: EpochCacheInterface, opts: { txsPermitted: boolean }) {
constructor(epochCache: EpochCacheInterface, opts: { txsPermitted: boolean; maxTxsPerBlock?: number }) {
super(epochCache, opts, 'p2p:block_proposal_validator');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export class CheckpointProposalValidator
extends ProposalValidator<CheckpointProposal>
implements P2PValidator<CheckpointProposal>
{
constructor(epochCache: EpochCacheInterface, opts: { txsPermitted: boolean }) {
constructor(epochCache: EpochCacheInterface, opts: { txsPermitted: boolean; maxTxsPerBlock?: number }) {
super(epochCache, opts, 'p2p:checkpoint_proposal_validator');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,16 @@ export abstract class ProposalValidator<TProposal extends BlockProposal | Checkp
protected epochCache: EpochCacheInterface;
protected logger: Logger;
protected txsPermitted: boolean;
protected maxTxsPerBlock?: number;

constructor(epochCache: EpochCacheInterface, opts: { txsPermitted: boolean }, loggerName: string) {
constructor(
epochCache: EpochCacheInterface,
opts: { txsPermitted: boolean; maxTxsPerBlock?: number },
loggerName: string,
) {
this.epochCache = epochCache;
this.txsPermitted = opts.txsPermitted;
this.maxTxsPerBlock = opts.maxTxsPerBlock;
this.logger = createLogger(loggerName);
}

Expand Down Expand Up @@ -47,6 +53,14 @@ export abstract class ProposalValidator<TProposal extends BlockProposal | Checkp
return { result: 'reject', severity: PeerErrorSeverity.MidToleranceError };
}

// Max txs per block check
if (this.maxTxsPerBlock !== undefined && proposal.txHashes.length > this.maxTxsPerBlock) {
this.logger.warn(
`Penalizing peer for proposal with ${proposal.txHashes.length} transaction(s) when max is ${this.maxTxsPerBlock}`,
);
return { result: 'reject', severity: PeerErrorSeverity.MidToleranceError };
}

// Embedded txs must be listed in txHashes
const hashSet = new Set(proposal.txHashes.map(h => h.toString()));
const missingTxHashes =
Expand Down
Loading
Loading