Skip to content

Commit

Permalink
fix: address issues when using wall-time (#8329)
Browse files Browse the repository at this point in the history
Fixes #8325.

- Specifies a salt for `e2e_l1_with_wall_time` to speed up the test
- For `e2e_p2p` will set a salt and set a block time + update how the
validator set is specified. There seemed to be connection issues with
the initial sequencer, so found it useful to only use the new set.
Adding a transfer to self to ensure that we wait until the sets are
updated.
- Only start the `watcher` if we are not using walltime (e.g., when
`l1BlockTime` is not specified).
  • Loading branch information
LHerskind authored Sep 3, 2024
1 parent 52258b1 commit 639fb3b
Show file tree
Hide file tree
Showing 12 changed files with 52 additions and 57 deletions.
18 changes: 7 additions & 11 deletions l1-contracts/src/core/Rollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -217,9 +217,6 @@ contract Rollup is Leonidas, IRollup, ITestRollup {
*
* @dev TODO(#7346): Verify root proofs rather than block root when batch rollups are integrated.
*
* @dev Will call `_progressState` to update the proven chain. Notice this have potentially
* unbounded gas consumption.
*
* @dev Will emit `L2ProofVerified` if the proof is valid
*
* @dev Will throw if:
Expand Down Expand Up @@ -368,16 +365,15 @@ contract Rollup is Leonidas, IRollup, ITestRollup {
}

/**
* @notice Check if a proposer can propose at a given time
* @notice Check if msg.sender can propose at a given time
*
* @param _ts - The timestamp to check
* @param _proposer - The proposer to check
* @param _archive - The archive to check (should be the latest archive)
*
* @return uint256 - The slot at the given timestamp
* @return uint256 - The block number at the given timestamp
*/
function canProposeAtTime(uint256 _ts, address _proposer, bytes32 _archive)
function canProposeAtTime(uint256 _ts, bytes32 _archive)
external
view
override(IRollup)
Expand All @@ -395,10 +391,10 @@ contract Rollup is Leonidas, IRollup, ITestRollup {
revert Errors.Rollup__InvalidArchive(tipArchive, _archive);
}

address proposer = getProposerAt(_ts);
if (proposer != address(0) && proposer != _proposer) {
revert Errors.Leonidas__InvalidProposer(proposer, _proposer);
}
SignatureLib.Signature[] memory sigs = new SignatureLib.Signature[](0);
DataStructures.ExecutionFlags memory flags =
DataStructures.ExecutionFlags({ignoreDA: true, ignoreSignatures: true});
_validateLeonidas(slot, sigs, _archive, flags);

return (slot, pendingBlockCount);
}
Expand Down Expand Up @@ -575,7 +571,7 @@ contract Rollup is Leonidas, IRollup, ITestRollup {
revert Errors.Rollup__InvalidEpoch(currentEpoch, epochNumber);
}

_proposePendingBlock(_slot, _signatures, _digest, _flags);
_validateLeonidas(_slot, _signatures, _digest, _flags);
}

/**
Expand Down
5 changes: 1 addition & 4 deletions l1-contracts/src/core/interfaces/IRollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@ interface IRollup {
event L2ProofVerified(uint256 indexed blockNumber, bytes32 indexed proverId);
event PrunedPending(uint256 provenBlockCount, uint256 pendingBlockCount);

function canProposeAtTime(uint256 _ts, address _proposer, bytes32 _archive)
external
view
returns (uint256, uint256);
function canProposeAtTime(uint256 _ts, bytes32 _archive) external view returns (uint256, uint256);
function validateHeader(
bytes calldata _header,
SignatureLib.Signature[] memory _signatures,
Expand Down
1 change: 1 addition & 0 deletions l1-contracts/src/core/libraries/ConstantsGen.sol
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ library Constants {
uint256 internal constant ETHEREUM_SLOT_DURATION = 12;
uint256 internal constant AZTEC_SLOT_DURATION = 12;
uint256 internal constant AZTEC_EPOCH_DURATION = 48;
uint256 internal constant AZTEC_TARGET_COMMITTEE_SIZE = 48;
uint256 internal constant GENESIS_ARCHIVE_ROOT =
8142738430000951296386584486068033372964809139261822027365426310856631083550;
uint256 internal constant FEE_JUICE_INITIAL_MINT = 20000000000;
Expand Down
4 changes: 2 additions & 2 deletions l1-contracts/src/core/sequencer_selection/Leonidas.sol
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ contract Leonidas is Ownable, ILeonidas {

// The target number of validators in a committee
// @todo #8021
uint256 public constant TARGET_COMMITTEE_SIZE = EPOCH_DURATION;
uint256 public constant TARGET_COMMITTEE_SIZE = Constants.AZTEC_TARGET_COMMITTEE_SIZE;

// The time that the contract was deployed
uint256 public immutable GENESIS_TIME;
Expand Down Expand Up @@ -344,7 +344,7 @@ contract Leonidas is Ownable, ILeonidas {
* @param _signatures - The signatures of the committee members
* @param _digest - The digest of the block
*/
function _proposePendingBlock(
function _validateLeonidas(
uint256 _slot,
SignatureLib.Signature[] memory _signatures,
bytes32 _digest,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ global ETHEREUM_SLOT_DURATION: u32 = 12;
// AZTEC_SLOT_DURATION should be a multiple of ETHEREUM_SLOT_DURATION
global AZTEC_SLOT_DURATION: u32 = ETHEREUM_SLOT_DURATION * 1;
global AZTEC_EPOCH_DURATION: u32 = 48;
global AZTEC_TARGET_COMMITTEE_SIZE: u32 = 48;
// The following is taken from building a block and looking at the `lastArchive` value in it.
// You can run the `integration_l1_publisher.test.ts` and look at the first blocks in the fixtures.
global GENESIS_ARCHIVE_ROOT: Field = 0x1200a06aae1368abe36530b585bd7a4d2ba4de5037b82076412691a187d7621e;
Expand Down
2 changes: 2 additions & 0 deletions yarn-project/circuits.js/src/constants.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export const BLOB_SIZE_IN_BYTES = 126976;
export const ETHEREUM_SLOT_DURATION = 12;
export const AZTEC_SLOT_DURATION = 12;
export const AZTEC_EPOCH_DURATION = 48;
export const AZTEC_TARGET_COMMITTEE_SIZE = 48;
export const GENESIS_ARCHIVE_ROOT = 8142738430000951296386584486068033372964809139261822027365426310856631083550n;
export const FEE_JUICE_INITIAL_MINT = 20000000000;
export const MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS = 20000;
Expand Down Expand Up @@ -395,6 +396,7 @@ export enum GeneratorIndex {
SIDE_EFFECT = 29,
FEE_PAYLOAD = 30,
COMBINED_PAYLOAD = 31,
TX_NULLIFIER = 32,
TX_REQUEST = 33,
SIGNATURE_PAYLOAD = 34,
VK = 41,
Expand Down
4 changes: 2 additions & 2 deletions yarn-project/end-to-end/src/e2e_l1_with_wall_time.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getSchnorrAccount } from '@aztec/accounts/schnorr';
import { type DebugLogger, Fr, GrumpkinScalar, type PXE, type SentTx, TxStatus } from '@aztec/aztec.js';
import { EthAddress } from '@aztec/circuits.js';
import { ETHEREUM_SLOT_DURATION, EthAddress } from '@aztec/circuits.js';
import { type PXEService } from '@aztec/pxe';

import { privateKeyToAccount } from 'viem/accounts';
Expand All @@ -16,7 +16,7 @@ describe('e2e_l1_with_wall_time', () => {
const account = privateKeyToAccount(`0x${getPrivateKeyFromIndex(0)!.toString('hex')}`);
const initialValidators = [EthAddress.fromString(account.address)];

({ teardown, logger, pxe } = await setup(0, { initialValidators, l1BlockTime: 12 }));
({ teardown, logger, pxe } = await setup(0, { initialValidators, l1BlockTime: ETHEREUM_SLOT_DURATION, salt: 420 }));
});

afterEach(() => teardown());
Expand Down
27 changes: 24 additions & 3 deletions yarn-project/end-to-end/src/e2e_p2p_network.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
TxStatus,
sleep,
} from '@aztec/aztec.js';
import { EthAddress } from '@aztec/circuits.js';
import { ETHEREUM_SLOT_DURATION, EthAddress } from '@aztec/circuits.js';
import { RollupAbi } from '@aztec/l1-artifacts';
import { type BootstrapNode } from '@aztec/p2p';
import { type PXEService, createPXEService, getPXEServiceConfig as getRpcConfig } from '@aztec/pxe';
Expand Down Expand Up @@ -60,7 +60,12 @@ describe('e2e_p2p_network', () => {

const initialValidators = [EthAddress.fromString(account.address)];

({ teardown, config, logger, deployL1ContractsValues } = await setup(0, { initialValidators, ...options }));
({ teardown, config, logger, deployL1ContractsValues } = await setup(0, {
initialValidators,
l1BlockTime: ETHEREUM_SLOT_DURATION,
salt: 420,
...options,
}));

bootstrapNode = await createBootstrapNode(BOOT_NODE_UDP_PORT);
bootstrapNodeEnr = bootstrapNode.getENR().encodeTxt();
Expand All @@ -77,15 +82,31 @@ describe('e2e_p2p_network', () => {
for (let i = 0; i < NUM_NODES; i++) {
const account = privateKeyToAccount(`0x${getPrivateKeyFromIndex(i + 1)!.toString('hex')}`);
await rollup.write.addValidator([account.address]);
logger.debug(`Adding ${account.address} as validator`);
}

// Remove the initial sequencer from the set! This was the sequencer we used for perform the setup.
logger.debug(`Removing ${account.address} as validator`);
const txHash = await rollup.write.removeValidator([account.address]);

await deployL1ContractsValues.publicClient.waitForTransactionReceipt({ hash: txHash });

//@note Now we jump ahead to the next epoch such that the validator committee is picked
// INTERVAL MINING: If we are using anvil interval mining this will NOT progress the time!
// Which means that the validator set will still be empty! So anyone can propose.
const slotsInEpoch = await rollup.read.EPOCH_DURATION();
const timestamp = await rollup.read.getTimestampForSlot([slotsInEpoch]);
const cheatCodes = new EthCheatCodes(config.l1RpcUrl);
await cheatCodes.warp(Number(timestamp));
try {
await cheatCodes.warp(Number(timestamp));
} catch (err) {
logger.debug('Warp failed, time already satisfied');
}

// Send and await a tx to make sure we mine a block for the warp to correctly progress.
await deployL1ContractsValues.publicClient.waitForTransactionReceipt({
hash: await deployL1ContractsValues.walletClient.sendTransaction({ to: account.address, value: 1n, account }),
});
});

afterEach(() => teardown());
Expand Down
6 changes: 5 additions & 1 deletion yarn-project/end-to-end/src/fixtures/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,11 @@ export async function setup(
deployL1ContractsValues.l1ContractAddresses.rollupAddress,
deployL1ContractsValues.publicClient,
);
watcher.start();

// If we are NOT using wall time, we should start the watcher to jump in time as needed.
if (!opts.l1BlockTime) {
watcher.start();
}

const wallets = numberOfAccounts > 0 ? await createAccounts(pxe, numberOfAccounts) : [];
const cheatCodes = CheatCodes.create(config.l1RpcUrl, pxe!);
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/end-to-end/src/fixtures/watcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export class Watcher {
this.logger.info(`Slot ${currentSlot} was filled, jumped to next slot`);
}
} catch (err) {
this.logger.error('mineIfSlotFilled failed', err);
this.logger.error('mineIfSlotFilled failed');
}
}
}
33 changes: 1 addition & 32 deletions yarn-project/sequencer-client/src/publisher/l1-publisher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,6 @@ export type L1SubmitProofArgs = {
aggregationObject: Buffer;
};

export type MetadataForSlot = {
proposer: EthAddress;
slot: bigint;
pendingBlockNumber: bigint;
archive: Buffer;
};

/**
* Publishes L2 blocks to L1. This implementation does *not* retry a transaction in
* the event of network congestion, but should work for local development.
Expand Down Expand Up @@ -176,11 +169,7 @@ export class L1Publisher {
*/
public async canProposeAtNextEthBlock(archive: Buffer): Promise<[bigint, bigint]> {
const ts = BigInt((await this.publicClient.getBlock()).timestamp + BigInt(ETHEREUM_SLOT_DURATION));
const [slot, blockNumber] = await this.rollupContract.read.canProposeAtTime([
ts,
this.account.address,
`0x${archive.toString('hex')}`,
]);
const [slot, blockNumber] = await this.rollupContract.read.canProposeAtTime([ts, `0x${archive.toString('hex')}`]);
return [slot, blockNumber];
}

Expand Down Expand Up @@ -227,26 +216,6 @@ export class L1Publisher {
}
}

// @note Assumes that all ethereum slots have blocks
// Using next Ethereum block so we do NOT need to wait for it being mined before seeing the effect
public async getMetadataForSlotAtNextEthBlock(): Promise<MetadataForSlot> {
const ts = BigInt((await this.publicClient.getBlock()).timestamp + BigInt(ETHEREUM_SLOT_DURATION));

const [submitter, slot, pendingBlockCount, archive] = await Promise.all([
this.rollupContract.read.getProposerAt([ts]),
this.rollupContract.read.getSlotAt([ts]),
this.rollupContract.read.pendingBlockCount(),
this.rollupContract.read.archive(),
]);

return {
proposer: EthAddress.fromString(submitter),
slot,
pendingBlockNumber: pendingBlockCount - 1n,
archive: Buffer.from(archive.replace('0x', ''), 'hex'),
};
}

public async getCurrentEpochCommittee(): Promise<EthAddress[]> {
const committee = await this.rollupContract.read.getCurrentEpochCommittee();
return committee.map(EthAddress.fromString);
Expand Down
6 changes: 5 additions & 1 deletion yarn-project/sequencer-client/src/sequencer/sequencer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,11 @@ export class Sequencer {
const revertError = err.walk(err => err instanceof ContractFunctionRevertedError);
if (revertError instanceof ContractFunctionRevertedError) {
const errorName = revertError.data?.errorName ?? '';
this.log.debug(`canProposeAtTime failed with "${errorName}"`);
const args =
revertError.metaMessages && revertError.metaMessages?.length > 1
? revertError.metaMessages[1].trimStart()
: '';
this.log.debug(`canProposeAtTime failed with "${errorName}${args}"`);
}
}
throw err;
Expand Down

0 comments on commit 639fb3b

Please sign in to comment.