diff --git a/docs/docs/dev_docs/testing/cheat_codes.md b/docs/docs/dev_docs/testing/cheat_codes.md index 134b59c1bd58..ab5d316efa3b 100644 --- a/docs/docs/dev_docs/testing/cheat_codes.md +++ b/docs/docs/dev_docs/testing/cheat_codes.md @@ -396,6 +396,9 @@ public async warp(to: number): Promise // 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 +// 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 + // Computes the slot value for a given map and key. public computeSlotInMap(baseSlot: Fr | bigint, key: Fr | bigint): Fr ``` @@ -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 +``` + +#### 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, +} + +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 bash + ## Participate Keep up with the latest discussion and join the conversation in the [Aztec forum](https://discourse.aztec.network). diff --git a/yarn-project/end-to-end/src/e2e_cheat_codes.test.ts b/yarn-project/end-to-end/src/e2e_cheat_codes.test.ts index 959a64858079..51aed8fb3b68 100644 --- a/yarn-project/end-to-end/src/e2e_cheat_codes.test.ts +++ b/yarn-project/end-to-end/src/e2e_cheat_codes.test.ts @@ -1,6 +1,18 @@ -import { CheatCodes, EthAddress, TxStatus, Wallet } from '@aztec/aztec.js'; +import { + AztecAddress, + 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'; @@ -8,25 +20,32 @@ 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; let walletClient: WalletClient; let publicClient: PublicClient; 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(); @@ -129,39 +148,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); }); });