Skip to content

Commit

Permalink
add warp to l2 cheatcode (#1479)
Browse files Browse the repository at this point in the history
Close #1275 - you can now change time on L2
Also add rollup address to node info

---------

Co-authored-by: Lasse Herskind <16536249+LHerskind@users.noreply.github.com>
  • Loading branch information
rahul-kothari and LHerskind authored Aug 10, 2023
1 parent 83b852e commit cf1f111
Show file tree
Hide file tree
Showing 13 changed files with 141 additions and 10 deletions.
4 changes: 4 additions & 0 deletions yarn-project/archiver/src/archiver/archiver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,10 @@ export class Archiver implements L2BlockSource, L2LogsSource, ContractDataSource
return Promise.resolve();
}

public getRollupAddress(): Promise<EthAddress> {
return Promise.resolve(this.rollupAddress);
}

/**
* Gets up to `limit` amount of L2 blocks starting from `from`.
* @param from - Number of the first block to return (inclusive).
Expand Down
15 changes: 14 additions & 1 deletion yarn-project/aztec-node/src/aztec-node/http-node.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AztecAddress, CircuitsWasm, Fr } from '@aztec/circuits.js';
import { AztecAddress, CircuitsWasm, EthAddress, Fr } from '@aztec/circuits.js';
import { randomBytes } from '@aztec/foundation/crypto';
import { Pedersen } from '@aztec/merkle-tree';
import {
Expand Down Expand Up @@ -145,6 +145,19 @@ describe('HttpNode', () => {
});
});

describe('getRollupAddress', () => {
it('should fetch and return the rollup address', async () => {
const addr = EthAddress.random();
const response = { rollupAddress: addr.toString() };
setFetchMock(response);

const result = await httpNode.getRollupAddress();

expect(fetch).toHaveBeenCalledWith(`${TEST_URL}get-rollup-address`);
expect(result).toEqual(addr);
});
});

describe('getContractData', () => {
it('should fetch and return contract public data', async () => {
const contractData = ContractPublicData.random();
Expand Down
8 changes: 8 additions & 0 deletions yarn-project/aztec-node/src/aztec-node/http-node.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
AztecAddress,
CONTRACT_TREE_HEIGHT,
EthAddress,
Fr,
L1_TO_L2_MSG_TREE_HEIGHT,
PRIVATE_DATA_TREE_HEIGHT,
Expand Down Expand Up @@ -98,6 +99,13 @@ export class HttpNode implements AztecNode {
return respJson.version;
}

public async getRollupAddress(): Promise<EthAddress> {
const url = new URL(`${this.baseUrl}/get-rollup-address`);
const response = await fetch(url.toString());
const respJson = await response.json();
return EthAddress.fromString(respJson.rollupAddress);
}

/**
* Method to fetch the chain id of the base-layer for the rollup.
* @returns The chain id.
Expand Down
9 changes: 9 additions & 0 deletions yarn-project/aztec-node/src/aztec-node/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Archiver } from '@aztec/archiver';
import {
CONTRACT_TREE_HEIGHT,
CircuitsWasm,
EthAddress,
Fr,
L1_TO_L2_MSG_TREE_HEIGHT,
PRIVATE_DATA_TREE_HEIGHT,
Expand Down Expand Up @@ -158,6 +159,14 @@ export class AztecNodeService implements AztecNode {
return Promise.resolve(this.chainId);
}

/**
* Method to fetch the rollup contract address at the base-layer.
* @returns The rollup address.
*/
public getRollupAddress(): Promise<EthAddress> {
return this.blockSource.getRollupAddress();
}

/**
* Lookup the L2 contract data for this contract.
* Contains the ethereum portal address and bytecode.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -289,11 +289,16 @@ export class AztecRPCServer implements AztecRPC {
}

public async getNodeInfo(): Promise<NodeInfo> {
const [version, chainId] = await Promise.all([this.node.getVersion(), this.node.getChainId()]);
const [version, chainId, rollupAddress] = await Promise.all([
this.node.getVersion(),
this.node.getChainId(),
this.node.getRollupAddress(),
]);

return {
version,
chainId,
rollupAddress,
};
}

Expand Down
2 changes: 1 addition & 1 deletion yarn-project/aztec.js/src/contract/contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe('Contract Class', () => {
const mockTxHash = { type: 'TxHash' } as any as TxHash;
const mockTxReceipt = { type: 'TxReceipt' } as any as TxReceipt;
const mockViewResultValue = 1;
const mockNodeInfo: NodeInfo = { version: 1, chainId: 2 };
const mockNodeInfo: NodeInfo = { version: 1, chainId: 2, rollupAddress: EthAddress.random() };

const defaultAbi: ContractAbi = {
name: 'FooContract',
Expand Down
57 changes: 50 additions & 7 deletions yarn-project/end-to-end/src/e2e_cheat_codes.test.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,35 @@
import { AztecNodeService } from '@aztec/aztec-node';
import { AztecRPCServer, EthAddress } from '@aztec/aztec-rpc';
import { AztecAddress, AztecRPCServer, EthAddress, Fr } from '@aztec/aztec-rpc';
import { Wallet } from '@aztec/aztec.js';
import { RollupAbi } from '@aztec/l1-artifacts';
import { LendingContract } from '@aztec/noir-contracts/types';
import { AztecRPC } from '@aztec/types';

import { Account, Chain, HttpTransport, PublicClient, WalletClient, parseEther } from 'viem';
import { Account, Chain, HttpTransport, PublicClient, WalletClient, getAddress, getContract, parseEther } from 'viem';

import { CheatCodes } from './fixtures/cheat_codes.js';
import { setup } from './fixtures/utils.js';

describe('e2e_cheat_codes', () => {
let aztecNode: AztecNodeService | undefined;
let aztecRpcServer: AztecRPC;

let wallet: Wallet;
let cc: CheatCodes;

let walletClient: WalletClient<HttpTransport, Chain, Account>;
let publicClient: PublicClient<HttpTransport, Chain>;
let rollupAddress: EthAddress;
let recipient: AztecAddress;

beforeAll(async () => {
let deployL1ContractsValues;
({ aztecNode, aztecRpcServer, cheatCodes: cc, deployL1ContractsValues } = await setup());
let accounts;
({ aztecNode, aztecRpcServer, wallet, accounts, cheatCodes: cc, deployL1ContractsValues } = await setup());

walletClient = deployL1ContractsValues.walletClient;
publicClient = deployL1ContractsValues.publicClient;
rollupAddress = deployL1ContractsValues.rollupAddress;
recipient = accounts[0];
}, 100_000);

afterAll(async () => {
Expand Down Expand Up @@ -87,7 +97,7 @@ describe('e2e_cheat_codes', () => {
// we will transfer 1 eth to a random address. Then impersonate the address to be able to send funds
// without impersonation we wouldn't be able to send funds.
const myAddress = (await walletClient.getAddresses())[0];
const randomAddress = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2';
const randomAddress = EthAddress.random().toString();
await walletClient.sendTransaction({
account: myAddress,
to: randomAddress,
Expand All @@ -99,12 +109,14 @@ describe('e2e_cheat_codes', () => {
await cc.l1.startPrank(EthAddress.fromString(randomAddress));
// send funds from random address
const amountToSend = parseEther('0.1');
await walletClient.sendTransaction({
const txHash = await walletClient.sendTransaction({
account: randomAddress,
to: myAddress,
value: amountToSend,
});
expect(await publicClient.getBalance({ address: randomAddress })).toBeLessThan(beforeBalance - amountToSend); // account for fees too
const tx = await publicClient.waitForTransactionReceipt({ hash: txHash });
const feePaid = tx.gasUsed * tx.effectiveGasPrice;
expect(await publicClient.getBalance({ address: randomAddress })).toBe(beforeBalance - amountToSend - feePaid);

// stop impersonating
await cc.l1.stopPrank(EthAddress.fromString(randomAddress));
Expand All @@ -122,5 +134,36 @@ describe('e2e_cheat_codes', () => {
expect(e.message).toContain('No Signer available');
}
});

it('can modify L1 block time', async () => {
// deploy lending contract
const tx = LendingContract.deploy(aztecRpcServer).send();
await tx.isMined({ interval: 0.1 });
const receipt = await tx.getReceipt();
const contract = await LendingContract.create(receipt.contractAddress!, wallet);

// now update time:
const timestamp = await cc.l1.timestamp();
const newTimestamp = timestamp + 100_000_000;
await cc.l2.warp(newTimestamp);

// ensure rollup contract is correctly updated
const rollup = getContract({ address: getAddress(rollupAddress.toString()), abi: RollupAbi, publicClient });
expect(Number(await rollup.read.lastBlockTs())).toEqual(newTimestamp);

// initialize contract -> this updates `lastUpdatedTs` to the current timestamp of the rollup
// this should be the new timestamp
const txInit = contract.methods.init().send({ origin: recipient });
await txInit.isMined({ interval: 0.1 });

// fetch last updated ts from L2 contract and expect it to me same as new timestamp
const lastUpdatedTs = Number(new Fr((await contract.methods.getTot(0).view())[0][1]).value);
expect(lastUpdatedTs).toEqual(newTimestamp);
// ensure anvil is correctly updated
expect(await cc.l1.timestamp()).toEqual(newTimestamp);
// ensure rollup contract is correctly updated
expect(Number(await rollup.read.lastBlockTs())).toEqual(newTimestamp);
expect;
}, 50_000);
});
});
13 changes: 13 additions & 0 deletions yarn-project/end-to-end/src/fixtures/cheat_codes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,19 @@ export class L2CheatCodes {
return await this.aztecRpc.getBlockNum();
}

/**
* Set time of the next execution on L2.
* It also modifies time on L1 for next execution and stores this time as the last rollup block on the rollup contract.
* @param to - The timestamp to set the next block to (must be greater than current time)
*/
public async warp(to: number): Promise<void> {
const rollupContract = (await this.aztecRpc.getNodeInfo()).rollupAddress;
await this.l1.setNextBlockTimestamp(to);
// 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.l1.store(rollupContract, 1n, BigInt(to));
}

/**
* Loads the value stored at the given slot in the public storage of the given contract.
* @param who - The address of the contract
Expand Down
9 changes: 9 additions & 0 deletions yarn-project/p2p/src/client/mocks.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { EthAddress } from '@aztec/circuits.js';
import { L2Block, L2BlockSource } from '@aztec/types';

/**
Expand All @@ -13,6 +14,14 @@ export class MockBlockSource implements L2BlockSource {
}
}

/**
* Method to fetch the rollup contract address at the base-layer.
* @returns The rollup address.
*/
getRollupAddress(): Promise<EthAddress> {
return Promise.resolve(EthAddress.random());
}

/**
* Gets the number of the latest L2 block processed by the block source implementation.
* @returns In this mock instance, returns the number of L2 blocks that we've mocked.
Expand Down
8 changes: 8 additions & 0 deletions yarn-project/rollup-provider/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,14 @@ export function appFactory(node: AztecNode, prefix: string) {
ctx.status = 200;
});

router.get('/get-rollup-address', async (ctx: Koa.Context) => {
ctx.set('content-type', 'application/json');
ctx.body = {
rollupAddress: (await node.getRollupAddress()).toString(),
};
ctx.status = 200;
});

router.get('/contract-data', async (ctx: Koa.Context) => {
const address = ctx.query.address;
ctx.set('content-type', 'application/json');
Expand Down
7 changes: 7 additions & 0 deletions yarn-project/types/src/interfaces/aztec-node.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { EthAddress } from '@aztec/circuits.js';
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { Fr } from '@aztec/foundation/fields';

Expand Down Expand Up @@ -59,6 +60,12 @@ export interface AztecNode extends DataCommitmentProvider, L1ToL2MessageProvider
*/
getChainId(): Promise<number>;

/**
* Method to fetch the rollup contract address at the base-layer.
* @returns The rollup address.
*/
getRollupAddress(): Promise<EthAddress>;

/**
* Lookup the L2 contract data for this contract.
* Contains the ethereum portal address and bytecode.
Expand Down
4 changes: 4 additions & 0 deletions yarn-project/types/src/interfaces/aztec_rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ export type NodeInfo = {
* The network's chain id.
*/
chainId: number;
/**
* The rollup contract address
*/
rollupAddress: EthAddress;
};

/** Provides up to which block has been synced by different components. */
Expand Down
8 changes: 8 additions & 0 deletions yarn-project/types/src/l2_block_source.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import { EthAddress } from '@aztec/circuits.js';

import { L2Block } from './l2_block.js';

/**
* Interface of classes allowing for the retrieval of L2 blocks.
*/
export interface L2BlockSource {
/**
* Method to fetch the rollup contract address at the base-layer.
* @returns The rollup address.
*/
getRollupAddress(): Promise<EthAddress>;

/**
* Gets the number of the latest L2 block processed by the block source implementation.
* @returns The number of the latest L2 block processed by the block source implementation.
Expand Down

0 comments on commit cf1f111

Please sign in to comment.