Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: load private tests and docs #3243

Merged
merged 2 commits into from
Nov 7, 2023
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: 40 additions & 0 deletions docs/docs/dev_docs/testing/cheat_codes.md
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,9 @@ public async warp(to: number): Promise<void>
// Loads the value stored at the given slot in the public storage of the given contract.
public async loadPublic(who: AztecAddress, slot: Fr | bigint): Promise<Fr>

// Loads the value stored at the given slot in the private storage of the given contract.
public async loadPrivate(owner: AztecAddress, contract: AztecAddress, slot: Fr | bigint): Promise<Note[]>

// Computes the slot value for a given map and key.
public computeSlotInMap(baseSlot: Fr | bigint, key: Fr | bigint): Fr
```
Expand Down Expand Up @@ -519,6 +522,43 @@ const slot = cc.aztec.computeSlotInMap(1n, key);
const value = await cc.aztec.loadPublic(address, slot);
```

### loadPrivate

#### Function Signature

```ts
public async loadPrivate(owner: AztecAddress, contract: AztecAddress, slot: Fr | bigint): Promise<Note[]>
```

#### Description

Loads the value stored at the given slot in the private storage of the given contract.

Note: One Field element occupies a storage slot. Hence, structs with multiple field elements will be spread over multiple sequential slots. Using loadPublic will only load a single field of the struct (depending on the size of the attributes within it).

#### Example
```rust
struct Storage {
...
pending_shields: Set<TransparentNote, TRANSPARENT_NOTE_LEN>,
}

impl Storage {
fn init() -> Self {
Storage {
...
pending_shields: Set::new(context, 5, TransparentNoteMethods),
}
}
}

contract Token {
...
}
```

#include_code load_private_cheatcode yarn-project/end-to-end/src/e2e_cheat_codes.test.ts typescript

## Participate

Keep up with the latest discussion and join the conversation in the [Aztec forum](https://discourse.aztec.network).
Expand Down
133 changes: 99 additions & 34 deletions yarn-project/end-to-end/src/e2e_cheat_codes.test.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,50 @@
import { CheatCodes, EthAddress, TxStatus, Wallet } from '@aztec/aztec.js';
import {
CheatCodes,
CompleteAddress,
EthAddress,
ExtendedNote,
Fr,
Note,
PXE,
TxStatus,
Wallet,
computeMessageSecretHash,
} from '@aztec/aztec.js';
import { RollupAbi } from '@aztec/l1-artifacts';
import { TestContract } from '@aztec/noir-contracts/types';
import { TestContract, TokenContract } from '@aztec/noir-contracts/types';

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

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

describe('e2e_cheat_codes', () => {
let wallet: Wallet;
let admin: CompleteAddress;
let cc: CheatCodes;
let pxe: PXE;
let teardown: () => Promise<void>;

let walletClient: WalletClient<HttpTransport, Chain, Account>;
let publicClient: PublicClient<HttpTransport, Chain>;
let rollupAddress: EthAddress;
let token: TokenContract;

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

walletClient = deployL1ContractsValues.walletClient;
publicClient = deployL1ContractsValues.publicClient;
rollupAddress = deployL1ContractsValues.l1ContractAddresses.rollupAddress;
admin = accounts[0];

token = await TokenContract.deploy(wallet, admin).send().deployed();
}, 100_000);

afterAll(() => teardown());

describe('L1 only', () => {
describe('L1 cheatcodes', () => {
describe('mine', () => {
it(`mine block`, async () => {
const blockNumber = await cc.eth.blockNumber();
Expand Down Expand Up @@ -129,39 +147,86 @@ describe('e2e_cheat_codes', () => {
expect(e.message).toContain('No Signer available');
}
});
});

it('can modify L2 block time', async () => {
const contract = await TestContract.deploy(wallet).send().deployed();
describe('L2 cheatcodes', () => {
describe('warp L2 Block Time', () => {
it('can modify L2 block time', async () => {
const contract = await TestContract.deploy(wallet).send().deployed();

// now update time:
const timestamp = await cc.eth.timestamp();
const newTimestamp = timestamp + 100_000_000;
await cc.aztec.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);
expect(Number(await rollup.read.lastWarpedBlockTs())).toEqual(newTimestamp);

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

// 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.is_time_equal(newTimestamp + 1).send();
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 () => {
const timestamp = await cc.eth.timestamp();
const pastTimestamp = timestamp - 1000;
await expect(async () => await cc.aztec.warp(pastTimestamp)).rejects.toThrow(
`Error setting next block timestamp: Timestamp error: ${pastTimestamp} is lower than or equal to previous block's timestamp`,
);
});
});

// now update time:
const timestamp = await cc.eth.timestamp();
const newTimestamp = timestamp + 100_000_000;
await cc.aztec.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);
expect(Number(await rollup.read.lastWarpedBlockTs())).toEqual(newTimestamp);

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

// 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.is_time_equal(newTimestamp + 1).send();
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('load public', async () => {
expect(await cc.aztec.loadPublic(token.address, 1n)).toEqual(admin.address.toField());
});

it('should throw if setting L2 block time to a past timestamp', async () => {
const timestamp = await cc.eth.timestamp();
const pastTimestamp = timestamp - 1000;
await expect(async () => await cc.aztec.warp(pastTimestamp)).rejects.toThrow(
`Error setting next block timestamp: Timestamp error: ${pastTimestamp} is lower than or equal to previous block's timestamp`,
);
it('load public returns 0 for non existent value', async () => {
const storageSlot = Fr.random();
expect(await cc.aztec.loadPublic(token.address, storageSlot)).toEqual(new Fr(0n));
});

it('load private works as expected for no notes', async () => {
const notes = await cc.aztec.loadPrivate(admin.address, token.address, 5n);
const values = notes.map(note => note.items[0]);
const balance = values.reduce((sum, current) => sum + current.toBigInt(), 0n);
expect(balance).toEqual(0n);
});

it('load private', async () => {
// mint note and check if it exists in pending_shield.
// docs:start:load_private_cheatcode
const mintAmount = 100n;
const secret = Fr.random();
const secretHash = await computeMessageSecretHash(secret);
const receipt = await token.methods.mint_private(mintAmount, secretHash).send().wait();

const note = new Note([new Fr(mintAmount), secretHash]);
const pendingShieldStorageSlot = new Fr(5n);
const extendedNote = new ExtendedNote(
note,
admin.address,
token.address,
pendingShieldStorageSlot,
receipt.txHash,
);
await pxe.addNote(extendedNote);

// check if note was added to pending shield:
const notes = await cc.aztec.loadPrivate(admin.address, token.address, pendingShieldStorageSlot);
const values = notes.map(note => note.items[0]);
const balance = values.reduce((sum, current) => sum + current.toBigInt(), 0n);
expect(balance).toEqual(mintAmount);
// docs:end:load_private_cheatcode
}, 50_000);
});
});