Skip to content

Commit 2b176b4

Browse files
authored
feat(public-vm): avm journal (AztecProtocol#3945)
1 parent 4f8326b commit 2b176b4

5 files changed

Lines changed: 302 additions & 49 deletions

File tree

yarn-project/acir-simulator/src/avm/avm_state_manager.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export class AvmStateManager {
2929
* @returns Avm State Manager
3030
*/
3131
public static rootStateManager(blockHeader: BlockHeader, hostStorage: HostStorage): AvmStateManager {
32-
const journal = new AvmJournal(hostStorage);
32+
const journal = AvmJournal.rootJournal(hostStorage);
3333
return new AvmStateManager(blockHeader, journal);
3434
}
3535

@@ -39,6 +39,7 @@ export class AvmStateManager {
3939
* @returns
4040
*/
4141
public static forkStateManager(parent: AvmStateManager): AvmStateManager {
42-
return new AvmStateManager(parent.blockHeader, parent.journal);
42+
const journal = AvmJournal.branchParent(parent.journal);
43+
return new AvmStateManager(parent.blockHeader, journal);
4344
}
4445
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**
2+
* Error thrown when a base journal is attempted to be merged.
3+
*/
4+
export class RootJournalCannotBeMerged extends Error {
5+
constructor() {
6+
super('Root journal cannot be merged');
7+
}
8+
}

yarn-project/acir-simulator/src/avm/journal/host_storage.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
import { CommitmentsDB, PublicContractsDB, PublicStateDB } from '../../index.js';
22

3-
/** - */
3+
/**
4+
* Host storage
5+
*
6+
* A wrapper around the node dbs
7+
*/
48
export class HostStorage {
59
/** - */
6-
public readonly stateDb: PublicStateDB;
10+
public readonly publicStateDb: PublicStateDB;
711
/** - */
812
public readonly contractsDb: PublicContractsDB;
9-
1013
/** - */
1114
public readonly commitmentsDb: CommitmentsDB;
1215

13-
constructor(stateDb: PublicStateDB, contractsDb: PublicContractsDB, commitmentsDb: CommitmentsDB) {
14-
this.stateDb = stateDb;
16+
constructor(publicStateDb: PublicStateDB, contractsDb: PublicContractsDB, commitmentsDb: CommitmentsDB) {
17+
this.publicStateDb = publicStateDb;
1518
this.contractsDb = contractsDb;
1619
this.commitmentsDb = commitmentsDb;
1720
}
Lines changed: 154 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,158 @@
1+
import { Fr } from '@aztec/foundation/fields';
2+
3+
import { MockProxy, mock } from 'jest-mock-extended';
4+
5+
import { CommitmentsDB, PublicContractsDB, PublicStateDB } from '../../index.js';
6+
import { HostStorage } from './host_storage.js';
7+
import { AvmJournal, JournalData } from './journal.js';
8+
19
describe('journal', () => {
2-
it('Should write to storage', () => {});
10+
let publicDb: MockProxy<PublicStateDB>;
11+
let journal: AvmJournal;
12+
13+
beforeEach(() => {
14+
publicDb = mock<PublicStateDB>();
15+
const commitmentsDb = mock<CommitmentsDB>();
16+
const contractsDb = mock<PublicContractsDB>();
17+
18+
const hostStorage = new HostStorage(publicDb, contractsDb, commitmentsDb);
19+
journal = new AvmJournal(hostStorage);
20+
});
21+
22+
describe('Public Storage', () => {
23+
it('Should cache write to storage', () => {
24+
// When writing to storage we should write to the storage writes map
25+
const contractAddress = new Fr(1);
26+
const key = new Fr(2);
27+
const value = new Fr(3);
28+
29+
journal.writeStorage(contractAddress, key, value);
30+
31+
const journalUpdates: JournalData = journal.flush();
32+
expect(journalUpdates.storageWrites.get(contractAddress)?.get(key)).toEqual(value);
33+
});
34+
35+
it('When reading from storage, should check the parent first', async () => {
36+
// Store a different value in storage vs the cache, and make sure the cache is returned
37+
const contractAddress = new Fr(1);
38+
const key = new Fr(2);
39+
const storedValue = new Fr(420);
40+
const parentValue = new Fr(69);
41+
const cachedValue = new Fr(1337);
42+
43+
publicDb.storageRead.mockResolvedValue(Promise.resolve(storedValue));
44+
45+
const childJournal = new AvmJournal(journal.hostStorage, journal);
46+
47+
// Get the cache miss
48+
const cacheMissResult = await childJournal.readStorage(contractAddress, key);
49+
expect(cacheMissResult).toEqual(storedValue);
50+
51+
// Write to storage
52+
journal.writeStorage(contractAddress, key, parentValue);
53+
const parentResult = await childJournal.readStorage(contractAddress, key);
54+
expect(parentResult).toEqual(parentValue);
55+
56+
// Get the parent value
57+
childJournal.writeStorage(contractAddress, key, cachedValue);
58+
59+
// Get the storage value
60+
const cachedResult = await childJournal.readStorage(contractAddress, key);
61+
expect(cachedResult).toEqual(cachedValue);
62+
});
63+
64+
it('When reading from storage, should check the cache first', async () => {
65+
// Store a different value in storage vs the cache, and make sure the cache is returned
66+
const contractAddress = new Fr(1);
67+
const key = new Fr(2);
68+
const storedValue = new Fr(420);
69+
const cachedValue = new Fr(69);
70+
71+
publicDb.storageRead.mockResolvedValue(Promise.resolve(storedValue));
72+
73+
// Get the cache first
74+
const cacheMissResult = await journal.readStorage(contractAddress, key);
75+
expect(cacheMissResult).toEqual(storedValue);
76+
77+
// Write to storage
78+
journal.writeStorage(contractAddress, key, cachedValue);
79+
80+
// Get the storage value
81+
const cachedResult = await journal.readStorage(contractAddress, key);
82+
expect(cachedResult).toEqual(cachedValue);
83+
});
84+
});
85+
86+
describe('UTXOs', () => {
87+
it('Should maintain commitments', () => {
88+
const utxo = new Fr(1);
89+
journal.writeCommitment(utxo);
90+
91+
const journalUpdates = journal.flush();
92+
expect(journalUpdates.newCommitments).toEqual([utxo]);
93+
});
94+
95+
it('Should maintain l1 messages', () => {
96+
const utxo = new Fr(1);
97+
journal.writeL1Message(utxo);
98+
99+
const journalUpdates = journal.flush();
100+
expect(journalUpdates.newL1Messages).toEqual([utxo]);
101+
});
102+
103+
it('Should maintain nullifiers', () => {
104+
const utxo = new Fr(1);
105+
journal.writeNullifier(utxo);
106+
107+
const journalUpdates = journal.flush();
108+
expect(journalUpdates.newNullifiers).toEqual([utxo]);
109+
});
110+
});
111+
112+
it('Should merge two journals together', async () => {
113+
// Fundamentally checking that insert ordering of public storage is preserved upon journal merge
114+
// time | journal | op | value
115+
// t0 -> journal0 -> write | 1
116+
// t1 -> journal1 -> write | 2
117+
// merge journals
118+
// t2 -> journal0 -> read | 2
119+
120+
const contractAddress = new Fr(1);
121+
const key = new Fr(2);
122+
const value = new Fr(1);
123+
const valueT1 = new Fr(2);
124+
const commitment = new Fr(10);
125+
const commitmentT1 = new Fr(20);
126+
127+
journal.writeStorage(contractAddress, key, value);
128+
journal.writeCommitment(commitment);
129+
journal.writeL1Message(commitment);
130+
journal.writeNullifier(commitment);
131+
132+
const journal1 = new AvmJournal(journal.hostStorage, journal);
133+
journal.writeStorage(contractAddress, key, valueT1);
134+
journal.writeCommitment(commitmentT1);
135+
journal.writeL1Message(commitmentT1);
136+
journal.writeNullifier(commitmentT1);
137+
138+
journal1.mergeWithParent();
139+
140+
// Check that the storage is merged by reading from the journal
141+
const result = await journal.readStorage(contractAddress, key);
142+
expect(result).toEqual(valueT1);
143+
144+
// Check that the UTXOs are merged
145+
const journalUpdates: JournalData = journal.flush();
146+
expect(journalUpdates.newCommitments).toEqual([commitment, commitmentT1]);
147+
expect(journalUpdates.newL1Messages).toEqual([commitment, commitmentT1]);
148+
expect(journalUpdates.newNullifiers).toEqual([commitment, commitmentT1]);
149+
});
3150

4-
it('Should read from storage', () => {});
151+
it('Cannot merge a root journal, but can merge a child journal', () => {
152+
const rootJournal = AvmJournal.rootJournal(journal.hostStorage);
153+
const childJournal = AvmJournal.branchParent(rootJournal);
5154

6-
it('Should merge two journals together', () => {});
155+
expect(() => rootJournal.mergeWithParent()).toThrow();
156+
expect(() => childJournal.mergeWithParent());
157+
});
7158
});

0 commit comments

Comments
 (0)