|
| 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 | + |
1 | 9 | 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 | + }); |
3 | 150 |
|
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); |
5 | 154 |
|
6 | | - it('Should merge two journals together', () => {}); |
| 155 | + expect(() => rootJournal.mergeWithParent()).toThrow(); |
| 156 | + expect(() => childJournal.mergeWithParent()); |
| 157 | + }); |
7 | 158 | }); |
0 commit comments