Skip to content

Commit

Permalink
feat: avm inserts nullifiers from private (#10129)
Browse files Browse the repository at this point in the history
- Insert nullifiers from private
- Rearrange forking in phase manager just a bit to make sure we always
fork before insertions of revertible private side effects
- Create inner helper writeSiloedNullifier in state manager
- Change NullifierManager/Cache to operate with siloed nullifiers and
simplify module a bunch
- Prep tests to compare nullifier root computed by ephemeral
trees/elsewhere

---------

Co-authored-by: Ilyas Ridhuan <[email protected]>
  • Loading branch information
dbanks12 and IlyasRidhuan authored Nov 27, 2024
1 parent 089c34c commit 3fc0c7c
Show file tree
Hide file tree
Showing 20 changed files with 606 additions and 502 deletions.
13 changes: 7 additions & 6 deletions yarn-project/simulator/src/avm/avm_simulator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
SerializableContractInstance,
} from '@aztec/circuits.js';
import { Grumpkin } from '@aztec/circuits.js/barretenberg';
import { computePublicDataTreeLeafSlot, computeVarArgsHash } from '@aztec/circuits.js/hash';
import { computePublicDataTreeLeafSlot, computeVarArgsHash, siloNullifier } from '@aztec/circuits.js/hash';
import { makeContractClassPublic, makeContractInstanceFromClassId } from '@aztec/circuits.js/testing';
import { FunctionSelector } from '@aztec/foundation/abi';
import { AztecAddress } from '@aztec/foundation/aztec-address';
Expand Down Expand Up @@ -534,6 +534,7 @@ describe('AVM simulator: transpiled Noir contracts', () => {
const listSlot1 = new Fr(listSlotNumber1);
const value0 = new Fr(420);
const value1 = new Fr(69);
const siloedNullifier0 = siloNullifier(address, value0);

let worldStateDB: WorldStateDB;
let trace: PublicSideEffectTraceInterface;
Expand Down Expand Up @@ -605,7 +606,7 @@ describe('AVM simulator: transpiled Noir contracts', () => {
const isPending = false;
// leafIndex is returned from DB call for nullifiers, so it is absent on DB miss
const _tracedLeafIndex = exists && !isPending ? leafIndex : Fr.ZERO;
expect(trace.traceNullifierCheck).toHaveBeenCalledWith(address, /*nullifier=*/ value0, exists);
expect(trace.traceNullifierCheck).toHaveBeenCalledWith(siloedNullifier0, exists);
});
});

Expand Down Expand Up @@ -671,7 +672,7 @@ describe('AVM simulator: transpiled Noir contracts', () => {
expect(results.output).toEqual([]);

expect(trace.traceNewNullifier).toHaveBeenCalledTimes(1);
expect(trace.traceNewNullifier).toHaveBeenCalledWith(expect.objectContaining(address), /*nullifier=*/ value0);
expect(trace.traceNewNullifier).toHaveBeenCalledWith(siloedNullifier0);
});

describe('Cached nullifiers', () => {
Expand All @@ -686,10 +687,10 @@ describe('AVM simulator: transpiled Noir contracts', () => {

// New nullifier and nullifier existence check should be traced
expect(trace.traceNewNullifier).toHaveBeenCalledTimes(1);
expect(trace.traceNewNullifier).toHaveBeenCalledWith(expect.objectContaining(address), /*nullifier=*/ value0);
expect(trace.traceNewNullifier).toHaveBeenCalledWith(siloedNullifier0);
expect(trace.traceNullifierCheck).toHaveBeenCalledTimes(1);
// leafIndex is returned from DB call for nullifiers, so it is absent on DB miss
expect(trace.traceNullifierCheck).toHaveBeenCalledWith(address, /*nullifier=*/ value0, /*exists=*/ true);
expect(trace.traceNullifierCheck).toHaveBeenCalledWith(siloedNullifier0, /*exists=*/ true);
});
it(`Emits same nullifier twice (expect failure)`, async () => {
const calldata = [value0];
Expand All @@ -703,7 +704,7 @@ describe('AVM simulator: transpiled Noir contracts', () => {

// Nullifier should be traced exactly once
expect(trace.traceNewNullifier).toHaveBeenCalledTimes(1);
expect(trace.traceNewNullifier).toHaveBeenCalledWith(expect.objectContaining(address), /*nullifier=*/ value0);
expect(trace.traceNewNullifier).toHaveBeenCalledWith(siloedNullifier0);
});
});

Expand Down
17 changes: 17 additions & 0 deletions yarn-project/simulator/src/avm/avm_tree.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,7 @@ describe('Batch Insertion', () => {
});
});

// This benchmark also performs a convenient sanity check
/* eslint no-console: ["error", { allow: ["time", "timeEnd"] }] */
describe('A basic benchmark', () => {
it('Should benchmark writes', async () => {
Expand All @@ -576,14 +577,30 @@ describe('A basic benchmark', () => {
const slots = leaves.map((_, i) => new Fr(i + 128));

const container = await AvmEphemeralForest.create(copyState);
await publicDataInsertWorldState(new Fr(0), new Fr(128));

// Updating the first slot, triggers the index 0 to be added to the minimum stored key in the container
await container.writePublicStorage(new Fr(0), new Fr(128));

// Check Roots before benchmarking
let wsRoot = await getWorldStateRoot(MerkleTreeId.PUBLIC_DATA_TREE);
let computedRoot = container.treeMap.get(MerkleTreeId.PUBLIC_DATA_TREE)!.getRoot();
expect(computedRoot.toBuffer()).toEqual(wsRoot);

console.time('benchmark');
// These writes are all new leaves and should be impacted by the key sorted algorithm of the tree.
for (let i = 0; i < leaves.length; i++) {
await container.writePublicStorage(slots[i], leaves[i]);
}
console.timeEnd('benchmark');

// Update worldstate for sanity check
for (let i = 0; i < leaves.length; i++) {
await publicDataInsertWorldState(slots[i], leaves[i]);
}
// Check roots
wsRoot = await getWorldStateRoot(MerkleTreeId.PUBLIC_DATA_TREE);
computedRoot = container.treeMap.get(MerkleTreeId.PUBLIC_DATA_TREE)!.getRoot();
expect(computedRoot.toBuffer()).toEqual(wsRoot);
});
});
Loading

0 comments on commit 3fc0c7c

Please sign in to comment.