Skip to content

Commit

Permalink
fix: increment time by 1 for previous rollup was warped (#1594)
Browse files Browse the repository at this point in the history
With Warp
```
L2 block 1: occurred at t = 100.
Call warp(200) => Rollup.sol's lastBlockTs = 200 & L1.setNextBlockTimeStamp = 200.
L2 block 2: txs show t = 200. Rollup published at t = 200 => Rollup.sol's lastBlockTs = 200
L2 block 3: txs show t = 200. 
```
Notice how txs in block 2 and block 3 show a timestamp of 200! This is
confusing.

So we check if the last rollup was warped (here block 2), and if so, txs
in the next rollup (block 3) should show ts = 201. We check if last
rollup was warped by introducing a variable in Rollup.sol that tracks
the last time block was warped.

Also Create #1614
  • Loading branch information
rahul-kothari authored Aug 25, 2023
1 parent 0e3914e commit 2a52107
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 13 deletions.
3 changes: 3 additions & 0 deletions l1-contracts/src/core/Rollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ contract Rollup is IRollup {

bytes32 public rollupStateHash;
uint256 public lastBlockTs;
// Tracks the last time time was warped on L2 ("warp" is the testing cheatocde).
// See https://github.com/AztecProtocol/aztec-packages/issues/1614
uint256 public lastWarpedBlockTs;

constructor(IRegistry _registry) {
VERIFIER = new MockVerifier();
Expand Down
2 changes: 2 additions & 0 deletions yarn-project/aztec.js/src/utils/cheat_codes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,8 @@ export class AztecCheatCodes {
// also store this time on the rollup contract (slot 1 tracks `lastBlockTs`).
// This is because when the sequencer executes public functions, it uses the timestamp stored in the rollup contract.
await this.eth.store(rollupContract, 1n, BigInt(to));
// also store this on slot 2 of the rollup contract (`lastWarpedBlockTs`) which tracks the last time warp was used.
await this.eth.store(rollupContract, 2n, BigInt(to));
}

/**
Expand Down
15 changes: 9 additions & 6 deletions yarn-project/end-to-end/src/e2e_cheat_codes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,16 +156,19 @@ describe('e2e_cheat_codes', () => {
// ensure rollup contract is correctly updated
const rollup = getContract({ address: getAddress(rollupAddress.toString()), abi: RollupAbi, publicClient });
expect(Number(await rollup.read.lastBlockTs())).toEqual(newTimestamp);
expect(Number(await rollup.read.lastWarpedBlockTs())).toEqual(newTimestamp);

const txIsTimeEqual = contract.methods.isTimeEqual(newTimestamp).send({ origin: recipient });
await txIsTimeEqual.isMined({ interval: 0.1 });
const isTimeEqualReceipt = await txIsTimeEqual.getReceipt();
const isTimeEqualReceipt = await txIsTimeEqual.wait({ interval: 0.1 });
expect(isTimeEqualReceipt.status).toBe(TxStatus.MINED);

const txTimeNotEqual = contract.methods.isTimeEqual(0).send({ origin: recipient });
await txTimeNotEqual.isMined({ interval: 0.1 });
const isTimeNotEqualReceipt = await txTimeNotEqual.getReceipt();
expect(isTimeNotEqualReceipt.status).toBe(TxStatus.DROPPED);
// Since last rollup block was warped, txs for this rollup will have time incremented by 1
// See https://github.com/AztecProtocol/aztec-packages/issues/1614 for details
const txTimeNotEqual = contract.methods.isTimeEqual(newTimestamp + 1).send({ origin: recipient });
const isTimeNotEqualReceipt = await txTimeNotEqual.wait({ interval: 0.1 });
expect(isTimeNotEqualReceipt.status).toBe(TxStatus.MINED);
// block is published at t >= newTimestamp + 1.
expect(Number(await rollup.read.lastBlockTs())).toBeGreaterThanOrEqual(newTimestamp + 1);
}, 50_000);

it('should throw if setting L2 block time to a past timestamp', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ export interface L1GlobalReader {
* @returns The chain id.
*/
getChainId(): Promise<bigint>;

/**
* Gets the current L1 time.
* @returns The current L1 time.
*/
getL1CurrentTime(): Promise<bigint>;

/**
* Gets the last time L2 was warped as tracked by the rollup contract.
* @returns The warped time.
*/
getLastWarpedBlockTs(): Promise<bigint>;
}

/**
Expand All @@ -35,10 +47,11 @@ export interface GlobalVariableBuilder {
}

/**
* Simple implementation of a builder that uses the minimum time possible for the global variables.
* Simple test implementation of a builder that uses the minimum time possible for the global variables.
* Also uses a "hack" to make use of the warp cheatcode that manipulates time on Aztec.
*/
export class SimpleGlobalVariableBuilder implements GlobalVariableBuilder {
private log = createDebugLogger('aztec:sequencer:simple_global_variable_builder');
export class SimpleTestGlobalVariableBuilder implements GlobalVariableBuilder {
private log = createDebugLogger('aztec:sequencer:simple_test_global_variable_builder');
constructor(private readonly reader: L1GlobalReader) {}

/**
Expand All @@ -47,10 +60,23 @@ export class SimpleGlobalVariableBuilder implements GlobalVariableBuilder {
* @returns The global variables for the given block number.
*/
public async buildGlobalVariables(blockNumber: Fr): Promise<GlobalVariables> {
const lastTimestamp = new Fr(await this.reader.getLastTimestamp());
let lastTimestamp = new Fr(await this.reader.getLastTimestamp());
const version = new Fr(await this.reader.getVersion());
const chainId = new Fr(await this.reader.getChainId());

// TODO(rahul) - fix #1614. By using the cheatcode warp to modify L2 time,
// txs in the next rollup would have same time as the txs in the current rollup (i.e. the rollup that was warped).
// So, for now you check if L2 time was warped and if so, serve warpedTime + 1 to txs in the new rollup.
// Check if L2 time was warped in the last rollup by checking if current L1 time is same as the warpedTime (stored on the rollup contract).
// more details at https://github.com/AztecProtocol/aztec-packages/issues/1614

const currTimestamp = await this.reader.getL1CurrentTime();
const rollupWarpTime = await this.reader.getLastWarpedBlockTs();
const isLastBlockWarped = rollupWarpTime === currTimestamp;
if (isLastBlockWarped) {
lastTimestamp = new Fr(lastTimestamp.value + 1n);
}

this.log(
`Built global variables for block ${blockNumber}: (${chainId}, ${version}, ${blockNumber}, ${lastTimestamp})`,
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { GlobalReaderConfig } from './config.js';
import { GlobalVariableBuilder, SimpleGlobalVariableBuilder } from './global_builder.js';
import { GlobalVariableBuilder, SimpleTestGlobalVariableBuilder } from './global_builder.js';
import { ViemReader } from './viem-reader.js';

export { SimpleGlobalVariableBuilder } from './global_builder.js';
export { SimpleTestGlobalVariableBuilder as SimpleGlobalVariableBuilder } from './global_builder.js';
export { GlobalReaderConfig } from './config.js';

/**
Expand All @@ -11,5 +11,5 @@ export { GlobalReaderConfig } from './config.js';
* @returns A new instance of the global variable builder.
*/
export function getGlobalVariableBuilder(config: GlobalReaderConfig): GlobalVariableBuilder {
return new SimpleGlobalVariableBuilder(new ViemReader(config));
return new SimpleTestGlobalVariableBuilder(new ViemReader(config));
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,12 @@ export class ViemReader implements L1GlobalReader {
public async getChainId(): Promise<bigint> {
return await Promise.resolve(BigInt(this.publicClient.chain.id));
}

public async getL1CurrentTime(): Promise<bigint> {
return await Promise.resolve((await this.publicClient.getBlock()).timestamp);
}

public async getLastWarpedBlockTs(): Promise<bigint> {
return BigInt(await this.rollupContract.read.lastWarpedBlockTs());
}
}

0 comments on commit 2a52107

Please sign in to comment.