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

chore: refactor e2e tests to use the new simulate fn #5854

Merged
merged 4 commits into from
Apr 19, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
21 changes: 5 additions & 16 deletions noir-projects/noir-contracts/contracts/auth_contract/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -33,32 +33,21 @@ contract Auth {
}

#[aztec(public)]
fn get_authorized() {
// We emit logs because we cannot otherwise return these values
emit_unencrypted_log(
&mut context,
storage.authorized.get_current_value_in_public()
);
fn get_authorized() -> AztecAddress {
storage.authorized.get_current_value_in_public()
}

#[aztec(public)]
fn get_scheduled_authorized() {
// We emit logs because we cannot otherwise return these values
emit_unencrypted_log(
&mut context,
storage.authorized.get_scheduled_value_in_public().0
);
fn get_scheduled_authorized() -> AztecAddress {
storage.authorized.get_scheduled_value_in_public().0
}

#[aztec(private)]
fn do_private_authorized_thing(value: Field) {
fn do_private_authorized_thing() {
// Reading a value from authorized in private automatically adds an extra validity condition: the base rollup
// circuit will reject this tx if included in a block past the block horizon, which is as far as the circuit can
// guarantee the value will not change from some historical value (due to CHANGE_AUTHORIZED_DELAY_BLOCKS).
let authorized = storage.authorized.get_current_value_in_private();
assert_eq(authorized, context.msg_sender(), "caller is not authorized");

// We emit logs because we cannot otherwise return these values
emit_unencrypted_log_from_private(&mut context, value);
}
}
14 changes: 4 additions & 10 deletions noir-projects/noir-contracts/contracts/test_contract/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ contract Test {
}

#[aztec(private)]
fn call_get_notes(storage_slot: Field, active_or_nullified: bool) {
fn call_get_notes(storage_slot: Field, active_or_nullified: bool) -> Field {
assert(
storage_slot != storage.example_constant.get_storage_slot(), "this storage slot is reserved for example_constant"
);
Expand All @@ -108,14 +108,11 @@ contract Test {

let opt_notes: [Option<ValueNote>; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL] = get_notes(&mut context, storage_slot, options);

// We can't get the return value of a private function from the outside world in an end to end test, so we
// expose it via an unecrypted log instead.
let value = opt_notes[0].unwrap().value;
emit_unencrypted_log_from_private(&mut context, value);
opt_notes[0].unwrap().value
}

#[aztec(private)]
fn call_get_notes_many(storage_slot: Field, active_or_nullified: bool) {
fn call_get_notes_many(storage_slot: Field, active_or_nullified: bool) -> [Field; 2] {
assert(
storage_slot != storage.example_constant.get_storage_slot(), "this storage slot is reserved for example_constant"
);
Expand All @@ -127,10 +124,7 @@ contract Test {

let opt_notes: [Option<ValueNote>; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL] = get_notes(&mut context, storage_slot, options);

// We can't get the return value of a private function from the outside world in an end to end test, so we
// expose it via an unecrypted log instead.
emit_unencrypted_log_from_private(&mut context, opt_notes[0].unwrap().value);
emit_unencrypted_log_from_private(&mut context, opt_notes[1].unwrap().value);
[opt_notes[0].unwrap().value, opt_notes[1].unwrap().value]
}

unconstrained fn call_view_notes(storage_slot: Field, active_or_nullified: bool) -> pub Field {
Expand Down
51 changes: 22 additions & 29 deletions yarn-project/end-to-end/src/e2e_auth_contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ describe('e2e_auth_contract', () => {

let contract: AuthContract;

const VALUE = 3;
const DELAY = 5;

beforeAll(async () => {
Expand Down Expand Up @@ -48,40 +47,30 @@ describe('e2e_auth_contract', () => {
}
}

async function assertLoggedAddress(interaction: ContractFunctionInteraction, address: AztecAddress) {
const logs = await pxe.getUnencryptedLogs({ txHash: (await interaction.send().wait()).txHash });
expect(AztecAddress.fromBuffer(logs.logs[0].log.data)).toEqual(address);
}

async function assertLoggedNumber(interaction: ContractFunctionInteraction, value: number) {
const logs = await pxe.getUnencryptedLogs({ txHash: (await interaction.send().wait()).txHash });
expect(Fr.fromBuffer(logs.logs[0].log.data)).toEqual(new Fr(value));
}

it('authorized is unset initially', async () => {
await assertLoggedAddress(contract.methods.get_authorized(), AztecAddress.ZERO);
expect(await contract.methods.get_authorized().simulate()).toEqual(AztecAddress.ZERO);
});

it('admin sets authorized', async () => {
await contract.withWallet(admin).methods.set_authorized(authorized.getAddress()).send().wait();

await assertLoggedAddress(contract.methods.get_scheduled_authorized(), authorized.getAddress());
expect(await contract.methods.get_scheduled_authorized().simulate()).toEqual(authorized.getAddress());
});

it('authorized is not yet set, cannot use permission', async () => {
await assertLoggedAddress(contract.methods.get_authorized(), AztecAddress.ZERO);
expect(await contract.methods.get_authorized().simulate()).toEqual(AztecAddress.ZERO);

await expect(
contract.withWallet(authorized).methods.do_private_authorized_thing(VALUE).send().wait(),
).rejects.toThrow('caller is not authorized');
await expect(contract.withWallet(authorized).methods.do_private_authorized_thing().send().wait()).rejects.toThrow(
'caller is not authorized',
);
});

it('after a while the scheduled change is effective and can be used with max block restriction', async () => {
await mineBlocks(DELAY); // This gets us past the block of change

await assertLoggedAddress(contract.methods.get_authorized(), authorized.getAddress());
expect(await contract.methods.get_authorized().simulate()).toEqual(authorized.getAddress());

const interaction = contract.withWallet(authorized).methods.do_private_authorized_thing(VALUE);
const interaction = contract.withWallet(authorized).methods.do_private_authorized_thing();

const tx = await interaction.prove();

Expand All @@ -94,32 +83,36 @@ describe('e2e_auth_contract', () => {
expect(tx.data.forRollup!.rollupValidationRequests.maxBlockNumber.isSome).toEqual(true);
expect(tx.data.forRollup!.rollupValidationRequests.maxBlockNumber.value).toEqual(new Fr(expectedMaxBlockNumber));

await assertLoggedNumber(interaction, VALUE);
expect((await interaction.send().wait()).status).toEqual('mined');
});

it('a new authorized address is set but not immediately effective, the previous one retains permissions', async () => {
await contract.withWallet(admin).methods.set_authorized(other.getAddress()).send().wait();

await assertLoggedAddress(contract.methods.get_authorized(), authorized.getAddress());
expect(await contract.methods.get_authorized().simulate()).toEqual(authorized.getAddress());

await assertLoggedAddress(contract.methods.get_scheduled_authorized(), other.getAddress());
expect(await contract.methods.get_scheduled_authorized().simulate()).toEqual(other.getAddress());

await expect(contract.withWallet(other).methods.do_private_authorized_thing(VALUE).send().wait()).rejects.toThrow(
await expect(contract.withWallet(other).methods.do_private_authorized_thing().send().wait()).rejects.toThrow(
'caller is not authorized',
);

await assertLoggedNumber(contract.withWallet(authorized).methods.do_private_authorized_thing(VALUE), VALUE);
expect((await contract.withWallet(authorized).methods.do_private_authorized_thing().send().wait()).status).toEqual(
'mined',
);
});

it('after some time the scheduled change is made effective', async () => {
await mineBlocks(DELAY); // This gets us past the block of change

await assertLoggedAddress(contract.methods.get_authorized(), other.getAddress());
expect(await contract.methods.get_authorized().simulate()).toEqual(other.getAddress());

await expect(
contract.withWallet(authorized).methods.do_private_authorized_thing(VALUE).send().wait(),
).rejects.toThrow('caller is not authorized');
await expect(contract.withWallet(authorized).methods.do_private_authorized_thing().send().wait()).rejects.toThrow(
'caller is not authorized',
);

await assertLoggedNumber(contract.withWallet(other).methods.do_private_authorized_thing(VALUE), VALUE);
expect((await contract.withWallet(other).methods.do_private_authorized_thing().send().wait()).status).toEqual(
'mined',
);
});
});
26 changes: 2 additions & 24 deletions yarn-project/end-to-end/src/e2e_note_getter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ describe('e2e_note_getter', () => {

async function assertNoteIsReturned(storageSlot: number, expectedValue: number, activeOrNullified: boolean) {
const viewNotesResult = await contract.methods.call_view_notes(storageSlot, activeOrNullified).simulate();
const getNotesResult = await callGetNotes(storageSlot, activeOrNullified);
const getNotesResult = await contract.methods.call_get_notes(storageSlot, activeOrNullified).simulate();

expect(viewNotesResult).toEqual(getNotesResult);
expect(viewNotesResult).toEqual(BigInt(expectedValue));
Expand All @@ -183,28 +183,6 @@ describe('e2e_note_getter', () => {
);
}

async function callGetNotes(storageSlot: number, activeOrNullified: boolean): Promise<bigint> {
// call_get_notes exposes the return value via an event since we cannot use simulate() with it.
const tx = contract.methods.call_get_notes(storageSlot, activeOrNullified).send();
await tx.wait();

const logs = (await tx.getUnencryptedLogs()).logs;
expect(logs.length).toBe(1);

return toBigInt(logs[0].log.data);
}

async function callGetNotesMany(storageSlot: number, activeOrNullified: boolean): Promise<Array<bigint>> {
// call_get_notes_many exposes the return values via event since we cannot use simulate() with it.
const tx = contract.methods.call_get_notes_many(storageSlot, activeOrNullified).send();
await tx.wait();

const logs = (await tx.getUnencryptedLogs()).logs;
expect(logs.length).toBe(2);

return [toBigInt(logs[0].log.data), toBigInt(logs[1].log.data)];
}

describe('active note only', () => {
const activeOrNullified = false;

Expand Down Expand Up @@ -250,7 +228,7 @@ describe('e2e_note_getter', () => {
const viewNotesManyResult = await contract.methods
.call_view_notes_many(storageSlot, activeOrNullified)
.simulate();
const getNotesManyResult = await callGetNotesMany(storageSlot, activeOrNullified);
const getNotesManyResult = await contract.methods.call_get_notes_many(storageSlot, activeOrNullified).simulate();

// We can't be sure in which order the notes will be returned, so we simply sort them to test equality. Note
// however that both view_notes and get_notes get the exact same result.
Expand Down
Loading