@aztec/simulator 0.23.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/dest/acvm/oracle/oracle.d.ts +2 -2
  2. package/dest/acvm/oracle/oracle.d.ts.map +1 -1
  3. package/dest/acvm/oracle/oracle.js +5 -5
  4. package/dest/acvm/oracle/typed_oracle.d.ts +2 -2
  5. package/dest/acvm/oracle/typed_oracle.d.ts.map +1 -1
  6. package/dest/acvm/oracle/typed_oracle.js +3 -3
  7. package/dest/avm/avm_execution_environment.d.ts +3 -2
  8. package/dest/avm/avm_execution_environment.d.ts.map +1 -1
  9. package/dest/avm/avm_execution_environment.js +6 -5
  10. package/dest/avm/avm_memory_types.d.ts +119 -38
  11. package/dest/avm/avm_memory_types.d.ts.map +1 -1
  12. package/dest/avm/avm_memory_types.js +91 -109
  13. package/dest/avm/avm_simulator.d.ts.map +1 -1
  14. package/dest/avm/avm_simulator.js +2 -3
  15. package/dest/avm/errors.d.ts +3 -1
  16. package/dest/avm/errors.d.ts.map +1 -1
  17. package/dest/avm/errors.js +9 -3
  18. package/dest/avm/fixtures/index.d.ts +4 -0
  19. package/dest/avm/fixtures/index.d.ts.map +1 -1
  20. package/dest/avm/fixtures/index.js +10 -2
  21. package/dest/avm/journal/host_storage.d.ts +1 -1
  22. package/dest/avm/journal/host_storage.d.ts.map +1 -1
  23. package/dest/avm/opcodes/addressing_mode.d.ts +24 -0
  24. package/dest/avm/opcodes/addressing_mode.d.ts.map +1 -0
  25. package/dest/avm/opcodes/addressing_mode.js +62 -0
  26. package/dest/avm/opcodes/comparators.d.ts.map +1 -1
  27. package/dest/avm/opcodes/comparators.js +8 -5
  28. package/dest/avm/opcodes/instruction.d.ts +4 -4
  29. package/dest/avm/opcodes/instruction.d.ts.map +1 -1
  30. package/dest/avm/opcodes/instruction.js +1 -1
  31. package/dest/avm/opcodes/memory.d.ts.map +1 -1
  32. package/dest/avm/opcodes/memory.js +5 -3
  33. package/dest/avm/serialization/bytecode_serialization.d.ts.map +1 -1
  34. package/dest/avm/serialization/bytecode_serialization.js +24 -22
  35. package/dest/avm/serialization/instruction_serialization.d.ts +17 -15
  36. package/dest/avm/serialization/instruction_serialization.d.ts.map +1 -1
  37. package/dest/avm/serialization/instruction_serialization.js +18 -16
  38. package/dest/avm/temporary_executor_migration.d.ts +25 -0
  39. package/dest/avm/temporary_executor_migration.d.ts.map +1 -0
  40. package/dest/avm/temporary_executor_migration.js +71 -0
  41. package/dest/client/client_execution_context.d.ts +4 -2
  42. package/dest/client/client_execution_context.d.ts.map +1 -1
  43. package/dest/client/client_execution_context.js +7 -4
  44. package/dest/client/execution_result.d.ts +2 -0
  45. package/dest/client/execution_result.d.ts.map +1 -1
  46. package/dest/client/execution_result.js +1 -1
  47. package/dest/client/simulator.d.ts +10 -5
  48. package/dest/client/simulator.d.ts.map +1 -1
  49. package/dest/client/simulator.js +19 -11
  50. package/dest/client/unconstrained_execution.js +2 -2
  51. package/dest/public/execution.js +2 -2
  52. package/dest/public/executor.d.ts +7 -0
  53. package/dest/public/executor.d.ts.map +1 -1
  54. package/dest/public/executor.js +26 -1
  55. package/dest/public/public_execution_context.js +2 -2
  56. package/dest/public/state_actions.d.ts +1 -1
  57. package/dest/public/state_actions.d.ts.map +1 -1
  58. package/dest/public/state_actions.js +5 -6
  59. package/dest/utils.d.ts +5 -20
  60. package/dest/utils.d.ts.map +1 -1
  61. package/dest/utils.js +4 -20
  62. package/package.json +7 -5
  63. package/src/acvm/acvm.ts +156 -0
  64. package/src/acvm/acvm_types.ts +11 -0
  65. package/src/acvm/deserialize.ts +36 -0
  66. package/src/acvm/index.ts +5 -0
  67. package/src/acvm/oracle/debug.ts +109 -0
  68. package/src/acvm/oracle/index.ts +17 -0
  69. package/src/acvm/oracle/oracle.ts +332 -0
  70. package/src/acvm/oracle/typed_oracle.ts +217 -0
  71. package/src/acvm/serialize.ts +75 -0
  72. package/src/avm/avm_context.ts +63 -0
  73. package/src/avm/avm_execution_environment.ts +98 -0
  74. package/src/avm/avm_machine_state.ts +93 -0
  75. package/src/avm/avm_memory_types.ts +309 -0
  76. package/src/avm/avm_message_call_result.ts +29 -0
  77. package/src/avm/avm_simulator.ts +89 -0
  78. package/src/avm/errors.ts +57 -0
  79. package/src/avm/fixtures/index.ts +90 -0
  80. package/src/avm/journal/host_storage.ts +20 -0
  81. package/src/avm/journal/index.ts +2 -0
  82. package/src/avm/journal/journal.ts +266 -0
  83. package/src/avm/opcodes/.eslintrc.cjs +8 -0
  84. package/src/avm/opcodes/accrued_substate.ts +92 -0
  85. package/src/avm/opcodes/addressing_mode.ts +66 -0
  86. package/src/avm/opcodes/arithmetic.ts +79 -0
  87. package/src/avm/opcodes/bitwise.ts +129 -0
  88. package/src/avm/opcodes/comparators.ts +72 -0
  89. package/src/avm/opcodes/control_flow.ts +129 -0
  90. package/src/avm/opcodes/environment_getters.ts +199 -0
  91. package/src/avm/opcodes/external_calls.ts +122 -0
  92. package/src/avm/opcodes/index.ts +10 -0
  93. package/src/avm/opcodes/instruction.ts +64 -0
  94. package/src/avm/opcodes/instruction_impl.ts +52 -0
  95. package/src/avm/opcodes/memory.ts +193 -0
  96. package/src/avm/opcodes/storage.ts +76 -0
  97. package/src/avm/serialization/buffer_cursor.ts +109 -0
  98. package/src/avm/serialization/bytecode_serialization.ts +172 -0
  99. package/src/avm/serialization/instruction_serialization.ts +167 -0
  100. package/src/avm/temporary_executor_migration.ts +108 -0
  101. package/src/client/client_execution_context.ts +472 -0
  102. package/src/client/db_oracle.ts +184 -0
  103. package/src/client/execution_note_cache.ts +90 -0
  104. package/src/client/execution_result.ts +89 -0
  105. package/src/client/index.ts +3 -0
  106. package/src/client/pick_notes.ts +125 -0
  107. package/src/client/private_execution.ts +78 -0
  108. package/src/client/simulator.ts +316 -0
  109. package/src/client/unconstrained_execution.ts +49 -0
  110. package/src/client/view_data_oracle.ts +243 -0
  111. package/src/common/errors.ts +61 -0
  112. package/src/common/index.ts +3 -0
  113. package/src/common/packed_args_cache.ts +55 -0
  114. package/src/common/side_effect_counter.ts +12 -0
  115. package/src/index.ts +3 -0
  116. package/src/public/db.ts +85 -0
  117. package/src/public/execution.ts +137 -0
  118. package/src/public/executor.ts +158 -0
  119. package/src/public/index.ts +9 -0
  120. package/src/public/public_execution_context.ts +217 -0
  121. package/src/public/state_actions.ts +100 -0
  122. package/src/test/utils.ts +38 -0
  123. package/src/utils.ts +18 -0
@@ -0,0 +1,158 @@
1
+ import { GlobalVariables, Header, PublicCircuitPublicInputs } from '@aztec/circuits.js';
2
+ import { createDebugLogger } from '@aztec/foundation/log';
3
+
4
+ import { Oracle, acvm, extractCallStack, extractReturnWitness } from '../acvm/index.js';
5
+ import { AvmContext } from '../avm/avm_context.js';
6
+ import { AvmMachineState } from '../avm/avm_machine_state.js';
7
+ import { AvmSimulator } from '../avm/avm_simulator.js';
8
+ import { HostStorage } from '../avm/journal/host_storage.js';
9
+ import { AvmWorldStateJournal } from '../avm/journal/index.js';
10
+ import {
11
+ temporaryConvertAvmResults,
12
+ temporaryCreateAvmExecutionEnvironment,
13
+ } from '../avm/temporary_executor_migration.js';
14
+ import { ExecutionError, createSimulationError } from '../common/errors.js';
15
+ import { SideEffectCounter } from '../common/index.js';
16
+ import { PackedArgsCache } from '../common/packed_args_cache.js';
17
+ import { AcirSimulator } from '../index.js';
18
+ import { CommitmentsDB, PublicContractsDB, PublicStateDB } from './db.js';
19
+ import { PublicExecution, PublicExecutionResult } from './execution.js';
20
+ import { PublicExecutionContext } from './public_execution_context.js';
21
+
22
+ /**
23
+ * Execute a public function and return the execution result.
24
+ */
25
+ export async function executePublicFunction(
26
+ context: PublicExecutionContext,
27
+ acir: Buffer,
28
+ log = createDebugLogger('aztec:simulator:public_execution'),
29
+ ): Promise<PublicExecutionResult> {
30
+ const execution = context.execution;
31
+ const { contractAddress, functionData } = execution;
32
+ const selector = functionData.selector;
33
+ log(`Executing public external function ${contractAddress.toString()}:${selector}`);
34
+
35
+ const initialWitness = context.getInitialWitness();
36
+ const acvmCallback = new Oracle(context);
37
+ const { partialWitness } = await acvm(await AcirSimulator.getSolver(), acir, initialWitness, acvmCallback).catch(
38
+ (err: Error) => {
39
+ throw new ExecutionError(
40
+ err.message,
41
+ {
42
+ contractAddress,
43
+ functionSelector: selector,
44
+ },
45
+ extractCallStack(err),
46
+ { cause: err },
47
+ );
48
+ },
49
+ );
50
+
51
+ const returnWitness = extractReturnWitness(acir, partialWitness);
52
+ const {
53
+ returnValues,
54
+ newL2ToL1Msgs,
55
+ newCommitments: newCommitmentsPadded,
56
+ newNullifiers: newNullifiersPadded,
57
+ } = PublicCircuitPublicInputs.fromFields(returnWitness);
58
+
59
+ const newL2ToL1Messages = newL2ToL1Msgs.filter(v => !v.isZero());
60
+ const newCommitments = newCommitmentsPadded.filter(v => !v.isEmpty());
61
+ const newNullifiers = newNullifiersPadded.filter(v => !v.isEmpty());
62
+
63
+ const { contractStorageReads, contractStorageUpdateRequests } = context.getStorageActionData();
64
+ log(
65
+ `Contract storage reads: ${contractStorageReads
66
+ .map(r => r.toFriendlyJSON() + ` - sec: ${r.sideEffectCounter}`)
67
+ .join(', ')}`,
68
+ );
69
+
70
+ const nestedExecutions = context.getNestedExecutions();
71
+ const unencryptedLogs = context.getUnencryptedLogs();
72
+
73
+ return {
74
+ execution,
75
+ newCommitments,
76
+ newL2ToL1Messages,
77
+ newNullifiers,
78
+ contractStorageReads,
79
+ contractStorageUpdateRequests,
80
+ returnValues,
81
+ nestedExecutions,
82
+ unencryptedLogs,
83
+ };
84
+ }
85
+
86
+ /**
87
+ * Handles execution of public functions.
88
+ */
89
+ export class PublicExecutor {
90
+ constructor(
91
+ private readonly stateDb: PublicStateDB,
92
+ private readonly contractsDb: PublicContractsDB,
93
+ private readonly commitmentsDb: CommitmentsDB,
94
+ private readonly header: Header,
95
+ ) {}
96
+
97
+ /**
98
+ * Executes a public execution request.
99
+ * @param execution - The execution to run.
100
+ * @param globalVariables - The global variables to use.
101
+ * @returns The result of the run plus all nested runs.
102
+ */
103
+ public async simulate(execution: PublicExecution, globalVariables: GlobalVariables): Promise<PublicExecutionResult> {
104
+ const selector = execution.functionData.selector;
105
+ const acir = await this.contractsDb.getBytecode(execution.contractAddress, selector);
106
+ if (!acir) {
107
+ throw new Error(`Bytecode not found for ${execution.contractAddress}:${selector}`);
108
+ }
109
+
110
+ // Functions can request to pack arguments before calling other functions.
111
+ // We use this cache to hold the packed arguments.
112
+ const packedArgs = PackedArgsCache.create([]);
113
+
114
+ const sideEffectCounter = new SideEffectCounter();
115
+
116
+ const context = new PublicExecutionContext(
117
+ execution,
118
+ this.header,
119
+ globalVariables,
120
+ packedArgs,
121
+ sideEffectCounter,
122
+ this.stateDb,
123
+ this.contractsDb,
124
+ this.commitmentsDb,
125
+ );
126
+
127
+ try {
128
+ return await executePublicFunction(context, acir);
129
+ } catch (err) {
130
+ throw createSimulationError(err instanceof Error ? err : new Error('Unknown error during public execution'));
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Executes a public execution request in the avm.
136
+ * @param execution - The execution to run.
137
+ * @param globalVariables - The global variables to use.
138
+ * @returns The result of the run plus all nested runs.
139
+ */
140
+ public async simulateAvm(
141
+ execution: PublicExecution,
142
+ globalVariables: GlobalVariables,
143
+ ): Promise<PublicExecutionResult> {
144
+ // Temporary code to construct the AVM context
145
+ // These data structures will permiate across the simulator when the public executor is phased out
146
+ const hostStorage = new HostStorage(this.stateDb, this.contractsDb, this.commitmentsDb);
147
+ const worldStateJournal = new AvmWorldStateJournal(hostStorage);
148
+ const executionEnv = temporaryCreateAvmExecutionEnvironment(execution, globalVariables);
149
+ const machineState = new AvmMachineState(0, 0, 0);
150
+
151
+ const context = new AvmContext(worldStateJournal, executionEnv, machineState);
152
+ const simulator = new AvmSimulator(context);
153
+
154
+ const result = await simulator.execute();
155
+ const newWorldState = context.worldState.flush();
156
+ return temporaryConvertAvmResults(execution, newWorldState, result);
157
+ }
158
+ }
@@ -0,0 +1,9 @@
1
+ export * from './db.js';
2
+ export {
3
+ PublicExecution,
4
+ PublicExecutionResult,
5
+ isPublicExecutionResult,
6
+ collectPublicDataReads,
7
+ collectPublicDataUpdateRequests,
8
+ } from './execution.js';
9
+ export { PublicExecutor } from './executor.js';
@@ -0,0 +1,217 @@
1
+ import { FunctionL2Logs, UnencryptedL2Log } from '@aztec/circuit-types';
2
+ import { CallContext, FunctionData, FunctionSelector, GlobalVariables, Header } from '@aztec/circuits.js';
3
+ import { AztecAddress } from '@aztec/foundation/aztec-address';
4
+ import { EthAddress } from '@aztec/foundation/eth-address';
5
+ import { Fr } from '@aztec/foundation/fields';
6
+ import { createDebugLogger } from '@aztec/foundation/log';
7
+
8
+ import { TypedOracle, toACVMWitness } from '../acvm/index.js';
9
+ import { PackedArgsCache, SideEffectCounter } from '../common/index.js';
10
+ import { CommitmentsDB, PublicContractsDB, PublicStateDB } from './db.js';
11
+ import { PublicExecution, PublicExecutionResult } from './execution.js';
12
+ import { executePublicFunction } from './executor.js';
13
+ import { ContractStorageActionsCollector } from './state_actions.js';
14
+
15
+ /**
16
+ * The execution context for a public tx simulation.
17
+ */
18
+ export class PublicExecutionContext extends TypedOracle {
19
+ private storageActions: ContractStorageActionsCollector;
20
+ private nestedExecutions: PublicExecutionResult[] = [];
21
+ private unencryptedLogs: UnencryptedL2Log[] = [];
22
+
23
+ constructor(
24
+ /**
25
+ * Data for this execution.
26
+ */
27
+ public readonly execution: PublicExecution,
28
+ private readonly header: Header,
29
+ private readonly globalVariables: GlobalVariables,
30
+ private readonly packedArgsCache: PackedArgsCache,
31
+ private readonly sideEffectCounter: SideEffectCounter,
32
+ private readonly stateDb: PublicStateDB,
33
+ private readonly contractsDb: PublicContractsDB,
34
+ private readonly commitmentsDb: CommitmentsDB,
35
+ private log = createDebugLogger('aztec:simulator:public_execution_context'),
36
+ ) {
37
+ super();
38
+ this.storageActions = new ContractStorageActionsCollector(stateDb, execution.contractAddress);
39
+ }
40
+
41
+ /**
42
+ * Generates the initial witness for a public function.
43
+ * @param args - The arguments to the function.
44
+ * @param callContext - The call context of the function.
45
+ * @param header - Contains data required to reconstruct a block hash (historical roots etc.).
46
+ * @param globalVariables - The global variables.
47
+ * @param witnessStartIndex - The index where to start inserting the parameters.
48
+ * @returns The initial witness.
49
+ */
50
+ public getInitialWitness(witnessStartIndex = 0) {
51
+ const { callContext, args } = this.execution;
52
+ const fields = [...callContext.toFields(), ...this.header.toFields(), ...this.globalVariables.toFields(), ...args];
53
+
54
+ return toACVMWitness(witnessStartIndex, fields);
55
+ }
56
+
57
+ /**
58
+ * Return the nested execution results during this execution.
59
+ */
60
+ public getNestedExecutions() {
61
+ return this.nestedExecutions;
62
+ }
63
+
64
+ /**
65
+ * Return the encrypted logs emitted during this execution.
66
+ */
67
+ public getUnencryptedLogs() {
68
+ return new FunctionL2Logs(this.unencryptedLogs.map(log => log.toBuffer()));
69
+ }
70
+
71
+ /**
72
+ * Return the data read and updated during this execution.
73
+ */
74
+ public getStorageActionData() {
75
+ const [contractStorageReads, contractStorageUpdateRequests] = this.storageActions.collect();
76
+ return { contractStorageReads, contractStorageUpdateRequests };
77
+ }
78
+
79
+ /**
80
+ * Pack the given arguments.
81
+ * @param args - Arguments to pack
82
+ */
83
+ public packArguments(args: Fr[]): Promise<Fr> {
84
+ return Promise.resolve(this.packedArgsCache.pack(args));
85
+ }
86
+
87
+ /**
88
+ * Fetches the a message from the db, given its key.
89
+ * @param msgKey - A buffer representing the message key.
90
+ * @returns The l1 to l2 message data
91
+ */
92
+ public async getL1ToL2Message(msgKey: Fr) {
93
+ return await this.commitmentsDb.getL1ToL2Message(msgKey);
94
+ }
95
+
96
+ /**
97
+ * Emit an unencrypted log.
98
+ * @param log - The unencrypted log to be emitted.
99
+ */
100
+ public emitUnencryptedLog(log: UnencryptedL2Log) {
101
+ // TODO(https://github.com/AztecProtocol/aztec-packages/issues/885)
102
+ this.unencryptedLogs.push(log);
103
+ this.log(`Emitted unencrypted log: "${log.toHumanReadable()}"`);
104
+ }
105
+
106
+ /**
107
+ * Retrieves the portal contract address associated with the given contract address.
108
+ * Returns zero address if the input contract address is not found or invalid.
109
+ * @param contractAddress - The address of the contract whose portal address is to be fetched.
110
+ * @returns The portal contract address.
111
+ */
112
+ public async getPortalContractAddress(contractAddress: AztecAddress) {
113
+ return (await this.contractsDb.getPortalContractAddress(contractAddress)) ?? EthAddress.ZERO;
114
+ }
115
+
116
+ /**
117
+ * Read the public storage data.
118
+ * @param startStorageSlot - The starting storage slot.
119
+ * @param numberOfElements - Number of elements to read from the starting storage slot.
120
+ */
121
+ public async storageRead(startStorageSlot: Fr, numberOfElements: number) {
122
+ const values = [];
123
+ for (let i = 0; i < Number(numberOfElements); i++) {
124
+ const storageSlot = new Fr(startStorageSlot.value + BigInt(i));
125
+ const sideEffectCounter = this.sideEffectCounter.count();
126
+ const value = await this.storageActions.read(storageSlot, sideEffectCounter);
127
+ this.log(`Oracle storage read: slot=${storageSlot.toString()} value=${value.toString()}`);
128
+ values.push(value);
129
+ }
130
+ return values;
131
+ }
132
+
133
+ /**
134
+ * Write some values to the public storage.
135
+ * @param startStorageSlot - The starting storage slot.
136
+ * @param values - The values to be written.
137
+ */
138
+ public async storageWrite(startStorageSlot: Fr, values: Fr[]) {
139
+ const newValues = [];
140
+ for (let i = 0; i < values.length; i++) {
141
+ const storageSlot = new Fr(startStorageSlot.value + BigInt(i));
142
+ const newValue = values[i];
143
+ const sideEffectCounter = this.sideEffectCounter.count();
144
+ this.storageActions.write(storageSlot, newValue, sideEffectCounter);
145
+ await this.stateDb.storageWrite(this.execution.contractAddress, storageSlot, newValue);
146
+ this.log(`Oracle storage write: slot=${storageSlot.toString()} value=${newValue.toString()}`);
147
+ newValues.push(newValue);
148
+ }
149
+ return newValues;
150
+ }
151
+
152
+ /**
153
+ * Calls a public function as a nested execution.
154
+ * @param targetContractAddress - The address of the contract to call.
155
+ * @param functionSelector - The function selector of the function to call.
156
+ * @param argsHash - The packed arguments to pass to the function.
157
+ * @returns The return values of the public function.
158
+ */
159
+ public async callPublicFunction(
160
+ targetContractAddress: AztecAddress,
161
+ functionSelector: FunctionSelector,
162
+ argsHash: Fr,
163
+ ) {
164
+ const args = this.packedArgsCache.unpack(argsHash);
165
+ this.log(`Public function call: addr=${targetContractAddress} selector=${functionSelector} args=${args.join(',')}`);
166
+
167
+ const portalAddress = (await this.contractsDb.getPortalContractAddress(targetContractAddress)) ?? EthAddress.ZERO;
168
+ const isInternal = await this.contractsDb.getIsInternal(targetContractAddress, functionSelector);
169
+ if (isInternal === undefined) {
170
+ throw new Error(`ERR: Method not found - ${targetContractAddress.toString()}:${functionSelector.toString()}`);
171
+ }
172
+
173
+ const acir = await this.contractsDb.getBytecode(targetContractAddress, functionSelector);
174
+ if (!acir) {
175
+ throw new Error(`Bytecode not found for ${targetContractAddress}:${functionSelector}`);
176
+ }
177
+
178
+ const functionData = new FunctionData(functionSelector, isInternal, false, false);
179
+
180
+ const callContext = CallContext.from({
181
+ msgSender: this.execution.contractAddress,
182
+ portalContractAddress: portalAddress,
183
+ storageContractAddress: targetContractAddress,
184
+ functionSelector,
185
+ isContractDeployment: false,
186
+ isDelegateCall: false,
187
+ isStaticCall: false,
188
+ startSideEffectCounter: 0, // TODO use counters in public execution
189
+ });
190
+
191
+ const nestedExecution: PublicExecution = {
192
+ args,
193
+ contractAddress: targetContractAddress,
194
+ functionData,
195
+ callContext,
196
+ };
197
+
198
+ const context = new PublicExecutionContext(
199
+ nestedExecution,
200
+ this.header,
201
+ this.globalVariables,
202
+ this.packedArgsCache,
203
+ this.sideEffectCounter,
204
+ this.stateDb,
205
+ this.contractsDb,
206
+ this.commitmentsDb,
207
+ this.log,
208
+ );
209
+
210
+ const childExecutionResult = await executePublicFunction(context, acir);
211
+
212
+ this.nestedExecutions.push(childExecutionResult);
213
+ this.log(`Returning from nested call: ret=${childExecutionResult.returnValues.join(', ')}`);
214
+
215
+ return childExecutionResult.returnValues;
216
+ }
217
+ }
@@ -0,0 +1,100 @@
1
+ import { ContractStorageRead, ContractStorageUpdateRequest } from '@aztec/circuits.js';
2
+ import { AztecAddress } from '@aztec/foundation/aztec-address';
3
+ import { Fr } from '@aztec/foundation/fields';
4
+
5
+ import { PublicStateDB } from './db.js';
6
+
7
+ /**
8
+ * Implements read/write operations on a contract public storage, collecting
9
+ * all read and update operations, and collapsing them into a single
10
+ * read or update per slot.
11
+ */
12
+ export class ContractStorageActionsCollector {
13
+ // Map from slot to first read value
14
+ private readonly contractStorageReads: Map<
15
+ bigint,
16
+ { /** The value read. */ currentValue: Fr; /** Side effect counter. */ sideEffectCounter: number }
17
+ > = new Map();
18
+
19
+ // Map from slot to first read value and latest updated value
20
+ private readonly contractStorageUpdateRequests: Map<
21
+ bigint,
22
+ {
23
+ /** The updated value. */ newValue: Fr;
24
+ /** Side effect counter. */ sideEffectCounter: number;
25
+ }
26
+ > = new Map();
27
+
28
+ constructor(private db: PublicStateDB, private address: AztecAddress) {}
29
+
30
+ /**
31
+ * Returns the current value of a slot according to the latest update request for it,
32
+ * falling back to the public db. Collects the operation in storage reads,
33
+ * as long as there is no existing update request.
34
+ * @param storageSlot - Slot to check.
35
+ * @param sideEffectCounter - Side effect counter associated with this storage action.
36
+ * @returns The current value as affected by all update requests so far.
37
+ */
38
+ public async read(storageSlot: Fr, sideEffectCounter: number): Promise<Fr> {
39
+ const slot = storageSlot.value;
40
+ const updateRequest = this.contractStorageUpdateRequests.get(slot);
41
+ if (updateRequest) {
42
+ return updateRequest.newValue;
43
+ }
44
+ const read = this.contractStorageReads.get(slot);
45
+ if (read) {
46
+ return read.currentValue;
47
+ }
48
+ const value = await this.db.storageRead(this.address, storageSlot);
49
+ this.contractStorageReads.set(slot, { currentValue: value, sideEffectCounter });
50
+ return value;
51
+ }
52
+
53
+ /**
54
+ * Sets a new value for a slot in the internal update requests cache,
55
+ * clearing any previous storage read or update operation for the same slot.
56
+ * @param storageSlot - Slot to write to.
57
+ * @param newValue - Value to write to it.
58
+ * @param sideEffectCounter - Side effect counter associated with this storage action.
59
+ */
60
+ public write(storageSlot: Fr, newValue: Fr, sideEffectCounter: number): void {
61
+ const slot = storageSlot.value;
62
+ const updateRequest = this.contractStorageUpdateRequests.get(slot);
63
+ if (updateRequest) {
64
+ this.contractStorageUpdateRequests.set(slot, { newValue, sideEffectCounter });
65
+ return;
66
+ }
67
+
68
+ const read = this.contractStorageReads.get(slot);
69
+ if (read) {
70
+ this.contractStorageReads.delete(slot);
71
+ this.contractStorageUpdateRequests.set(slot, { newValue, sideEffectCounter });
72
+ return;
73
+ }
74
+
75
+ this.contractStorageUpdateRequests.set(slot, { newValue, sideEffectCounter });
76
+ return;
77
+ }
78
+
79
+ /**
80
+ * Returns all storage reads and update requests performed.
81
+ * @returns All storage read and update requests.
82
+ */
83
+ public collect(): [ContractStorageRead[], ContractStorageUpdateRequest[]] {
84
+ const reads = Array.from(this.contractStorageReads.entries()).map(([slot, valueAndCounter]) =>
85
+ ContractStorageRead.from({
86
+ storageSlot: new Fr(slot),
87
+ ...valueAndCounter,
88
+ }),
89
+ );
90
+
91
+ const updateRequests = Array.from(this.contractStorageUpdateRequests.entries()).map(([slot, valuesAndCounter]) =>
92
+ ContractStorageUpdateRequest.from({
93
+ storageSlot: new Fr(slot),
94
+ ...valuesAndCounter,
95
+ }),
96
+ );
97
+
98
+ return [reads, updateRequests];
99
+ }
100
+ }
@@ -0,0 +1,38 @@
1
+ import { L1Actor, L1ToL2Message, L2Actor } from '@aztec/circuit-types';
2
+ import { AztecAddress, EthAddress, Fr } from '@aztec/circuits.js';
3
+ import { computeSecretMessageHash } from '@aztec/circuits.js/abis';
4
+ import { sha256 } from '@aztec/foundation/crypto';
5
+
6
+ /**
7
+ * Test utility function to craft an L1 to L2 message.
8
+ * @param selector - The cross chain message selector.
9
+ * @param contentPreimage - The args after the selector.
10
+ * @param targetContract - The contract to consume the message.
11
+ * @param secret - The secret to unlock the message.
12
+ * @returns The L1 to L2 message.
13
+ */
14
+ export const buildL1ToL2Message = (
15
+ selector: string,
16
+ contentPreimage: Fr[],
17
+ targetContract: AztecAddress,
18
+ secret: Fr,
19
+ ) => {
20
+ // Write the selector into a buffer.
21
+ const selectorBuf = Buffer.from(selector, 'hex');
22
+
23
+ const contentBuf = Buffer.concat([selectorBuf, ...contentPreimage.map(field => field.toBuffer())]);
24
+ const content = Fr.fromBufferReduce(sha256(contentBuf));
25
+
26
+ const secretHash = computeSecretMessageHash(secret);
27
+
28
+ // Eventually the kernel will need to prove the kernel portal pair exists within the contract tree,
29
+ // EthAddress.random() will need to be replaced when this happens
30
+ return new L1ToL2Message(
31
+ new L1Actor(EthAddress.random(), 1),
32
+ new L2Actor(targetContract, 1),
33
+ content,
34
+ secretHash,
35
+ 0,
36
+ 0,
37
+ );
38
+ };
package/src/utils.ts ADDED
@@ -0,0 +1,18 @@
1
+ import { pedersenHash } from '@aztec/foundation/crypto';
2
+ import { Fr } from '@aztec/foundation/fields';
3
+
4
+ /**
5
+ * Computes the resulting storage slot for an entry in a mapping.
6
+ * @param mappingSlot - The slot of the mapping within state.
7
+ * @param key - The key of the mapping.
8
+ * @returns The slot in the contract storage where the value is stored.
9
+ */
10
+ export function computeSlotForMapping(
11
+ mappingSlot: Fr,
12
+ key: {
13
+ /** Serialize to a field. */
14
+ toField: () => Fr;
15
+ },
16
+ ) {
17
+ return Fr.fromBuffer(pedersenHash([mappingSlot, key.toField()].map(field => field.toBuffer())));
18
+ }