@aztec/simulator 0.67.0 → 0.67.1-devnet
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.
- package/dest/acvm/oracle/oracle.d.ts +1 -1
- package/dest/acvm/oracle/oracle.d.ts.map +1 -1
- package/dest/acvm/oracle/oracle.js +3 -3
- package/dest/acvm/oracle/typed_oracle.d.ts +1 -1
- package/dest/acvm/oracle/typed_oracle.d.ts.map +1 -1
- package/dest/acvm/oracle/typed_oracle.js +3 -3
- package/dest/avm/avm_memory_types.d.ts +1 -1
- package/dest/avm/avm_memory_types.d.ts.map +1 -1
- package/dest/avm/avm_memory_types.js +27 -27
- package/dest/avm/avm_simulator.d.ts.map +1 -1
- package/dest/avm/avm_simulator.js +6 -3
- package/dest/avm/avm_tree.d.ts +2 -1
- package/dest/avm/avm_tree.d.ts.map +1 -1
- package/dest/avm/avm_tree.js +6 -2
- package/dest/avm/fixtures/index.d.ts +2 -0
- package/dest/avm/fixtures/index.d.ts.map +1 -1
- package/dest/avm/fixtures/index.js +4 -4
- package/dest/avm/journal/journal.d.ts +16 -4
- package/dest/avm/journal/journal.d.ts.map +1 -1
- package/dest/avm/journal/journal.js +32 -14
- package/dest/avm/opcodes/conversion.d.ts +4 -4
- package/dest/avm/opcodes/conversion.d.ts.map +1 -1
- package/dest/avm/opcodes/conversion.js +22 -18
- package/dest/avm/test_utils.d.ts +1 -0
- package/dest/avm/test_utils.d.ts.map +1 -1
- package/dest/avm/test_utils.js +4 -1
- package/dest/client/client_execution_context.d.ts.map +1 -1
- package/dest/client/client_execution_context.js +2 -1
- package/dest/client/db_oracle.d.ts +7 -3
- package/dest/client/db_oracle.d.ts.map +1 -1
- package/dest/client/index.d.ts +1 -0
- package/dest/client/index.d.ts.map +1 -1
- package/dest/client/index.js +2 -1
- package/dest/client/view_data_oracle.d.ts +2 -2
- package/dest/client/view_data_oracle.d.ts.map +1 -1
- package/dest/client/view_data_oracle.js +5 -4
- package/dest/providers/acvm_wasm.js +2 -2
- package/dest/providers/index.d.ts +0 -1
- package/dest/providers/index.d.ts.map +1 -1
- package/dest/providers/index.js +1 -2
- package/dest/public/enqueued_call_side_effect_trace.d.ts +4 -3
- package/dest/public/enqueued_call_side_effect_trace.d.ts.map +1 -1
- package/dest/public/enqueued_call_side_effect_trace.js +6 -5
- package/dest/public/execution.d.ts +2 -2
- package/dest/public/execution.d.ts.map +1 -1
- package/dest/public/execution.js +1 -1
- package/dest/public/fee_payment.d.ts.map +1 -1
- package/dest/public/fee_payment.js +4 -3
- package/dest/public/fixtures/index.js +3 -3
- package/dest/public/public_db_sources.d.ts.map +1 -1
- package/dest/public/public_db_sources.js +7 -6
- package/dest/public/public_processor.d.ts +5 -4
- package/dest/public/public_processor.d.ts.map +1 -1
- package/dest/public/public_processor.js +65 -61
- package/dest/public/public_processor_metrics.d.ts +1 -1
- package/dest/public/public_processor_metrics.d.ts.map +1 -1
- package/dest/public/public_tx_context.d.ts.map +1 -1
- package/dest/public/public_tx_context.js +32 -20
- package/dest/public/public_tx_simulator.d.ts.map +1 -1
- package/dest/public/public_tx_simulator.js +12 -1
- package/dest/public/side_effect_trace_interface.d.ts +2 -1
- package/dest/public/side_effect_trace_interface.d.ts.map +1 -1
- package/dest/public/transitional_adapters.d.ts.map +1 -1
- package/dest/public/transitional_adapters.js +2 -35
- package/package.json +9 -9
- package/src/acvm/oracle/oracle.ts +2 -2
- package/src/acvm/oracle/typed_oracle.ts +2 -2
- package/src/avm/avm_memory_types.ts +29 -27
- package/src/avm/avm_simulator.ts +4 -2
- package/src/avm/avm_tree.ts +6 -1
- package/src/avm/fixtures/index.ts +4 -2
- package/src/avm/journal/journal.ts +41 -8
- package/src/avm/opcodes/conversion.ts +21 -16
- package/src/avm/test_utils.ts +4 -0
- package/src/client/client_execution_context.ts +2 -0
- package/src/client/db_oracle.ts +8 -3
- package/src/client/index.ts +1 -0
- package/src/client/view_data_oracle.ts +6 -3
- package/src/providers/acvm_wasm.ts +2 -2
- package/src/providers/index.ts +0 -1
- package/src/public/enqueued_call_side_effect_trace.ts +8 -12
- package/src/public/execution.ts +1 -2
- package/src/public/fee_payment.ts +3 -2
- package/src/public/fixtures/index.ts +2 -2
- package/src/public/public_db_sources.ts +6 -5
- package/src/public/public_processor.ts +83 -78
- package/src/public/public_processor_metrics.ts +1 -1
- package/src/public/public_tx_context.ts +48 -21
- package/src/public/public_tx_simulator.ts +11 -0
- package/src/public/side_effect_trace_interface.ts +2 -1
- package/src/public/transitional_adapters.ts +0 -51
|
@@ -42,7 +42,6 @@ import {
|
|
|
42
42
|
PublicDataWrite,
|
|
43
43
|
ScopedL2ToL1Message,
|
|
44
44
|
ScopedLogHash,
|
|
45
|
-
type ScopedNoteHash,
|
|
46
45
|
SerializableContractInstance,
|
|
47
46
|
type TreeSnapshots,
|
|
48
47
|
} from '@aztec/circuits.js';
|
|
@@ -74,7 +73,7 @@ export type SideEffects = {
|
|
|
74
73
|
enqueuedCalls: PublicCallRequest[];
|
|
75
74
|
|
|
76
75
|
publicDataWrites: PublicDataUpdateRequest[];
|
|
77
|
-
noteHashes:
|
|
76
|
+
noteHashes: NoteHash[];
|
|
78
77
|
nullifiers: Nullifier[];
|
|
79
78
|
l2ToL1Msgs: ScopedL2ToL1Message[];
|
|
80
79
|
|
|
@@ -111,7 +110,7 @@ export class PublicEnqueuedCallSideEffectTrace implements PublicSideEffectTraceI
|
|
|
111
110
|
private publicDataWrites: PublicDataUpdateRequest[] = [];
|
|
112
111
|
private protocolPublicDataWritesLength: number = 0;
|
|
113
112
|
private userPublicDataWritesLength: number = 0;
|
|
114
|
-
private noteHashes:
|
|
113
|
+
private noteHashes: NoteHash[] = [];
|
|
115
114
|
private nullifiers: Nullifier[] = [];
|
|
116
115
|
private l2ToL1Messages: ScopedL2ToL1Message[] = [];
|
|
117
116
|
private unencryptedLogs: UnencryptedL2Log[] = [];
|
|
@@ -194,6 +193,10 @@ export class PublicEnqueuedCallSideEffectTrace implements PublicSideEffectTraceI
|
|
|
194
193
|
this.sideEffectCounter++;
|
|
195
194
|
}
|
|
196
195
|
|
|
196
|
+
public getNoteHashCount() {
|
|
197
|
+
return this.previousSideEffectArrayLengths.noteHashes + this.noteHashes.length;
|
|
198
|
+
}
|
|
199
|
+
|
|
197
200
|
public tracePublicStorageRead(
|
|
198
201
|
contractAddress: AztecAddress,
|
|
199
202
|
slot: Fr,
|
|
@@ -272,19 +275,12 @@ export class PublicEnqueuedCallSideEffectTrace implements PublicSideEffectTraceI
|
|
|
272
275
|
// NOTE: counter does not increment for note hash checks (because it doesn't rely on pending note hashes)
|
|
273
276
|
}
|
|
274
277
|
|
|
275
|
-
public traceNewNoteHash(
|
|
276
|
-
contractAddress: AztecAddress,
|
|
277
|
-
noteHash: Fr,
|
|
278
|
-
leafIndex: Fr = Fr.zero(),
|
|
279
|
-
path: Fr[] = emptyNoteHashPath(),
|
|
280
|
-
) {
|
|
278
|
+
public traceNewNoteHash(noteHash: Fr, leafIndex: Fr = Fr.zero(), path: Fr[] = emptyNoteHashPath()) {
|
|
281
279
|
if (this.noteHashes.length + this.previousSideEffectArrayLengths.noteHashes >= MAX_NOTE_HASHES_PER_TX) {
|
|
282
280
|
throw new SideEffectLimitReachedError('note hash', MAX_NOTE_HASHES_PER_TX);
|
|
283
281
|
}
|
|
284
282
|
|
|
285
|
-
|
|
286
|
-
//const siloedNoteHash = siloNoteHash(contractAddress, noteHash);
|
|
287
|
-
this.noteHashes.push(new NoteHash(noteHash, this.sideEffectCounter).scope(contractAddress));
|
|
283
|
+
this.noteHashes.push(new NoteHash(noteHash, this.sideEffectCounter));
|
|
288
284
|
this.log.debug(`NEW_NOTE_HASH cnt: ${this.sideEffectCounter}`);
|
|
289
285
|
this.avmCircuitHints.noteHashWrites.items.push(new AvmAppendTreeHint(leafIndex, noteHash, path));
|
|
290
286
|
this.incrementSideEffectCounter();
|
package/src/public/execution.ts
CHANGED
|
@@ -20,7 +20,6 @@ import {
|
|
|
20
20
|
RevertCode,
|
|
21
21
|
type ScopedL2ToL1Message,
|
|
22
22
|
type ScopedLogHash,
|
|
23
|
-
type ScopedNoteHash,
|
|
24
23
|
type TreeLeafReadRequest,
|
|
25
24
|
} from '@aztec/circuits.js';
|
|
26
25
|
import { computeVarArgsHash } from '@aztec/circuits.js/hash';
|
|
@@ -29,7 +28,7 @@ export interface PublicSideEffects {
|
|
|
29
28
|
/** The contract storage update requests performed. */
|
|
30
29
|
publicDataWrites: PublicDataUpdateRequest[];
|
|
31
30
|
/** The new note hashes to be inserted into the note hashes tree. */
|
|
32
|
-
noteHashes:
|
|
31
|
+
noteHashes: NoteHash[];
|
|
33
32
|
/** The new nullifiers to be inserted into the nullifier tree. */
|
|
34
33
|
nullifiers: Nullifier[];
|
|
35
34
|
/** The new l2 to l1 messages generated to be inserted into the messages tree. */
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { computePublicDataTreeLeafSlot, deriveStorageSlotInMap } from '@aztec/circuits.js/hash';
|
|
2
2
|
import { type AztecAddress } from '@aztec/foundation/aztec-address';
|
|
3
3
|
import { Fr } from '@aztec/foundation/fields';
|
|
4
|
-
import { ProtocolContractAddress
|
|
4
|
+
import { ProtocolContractAddress } from '@aztec/protocol-contracts';
|
|
5
|
+
import { FeeJuiceArtifact } from '@aztec/protocol-contracts/fee-juice';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Computes the storage slot within the Fee Juice contract for the balance of the fee payer.
|
|
8
9
|
*/
|
|
9
10
|
export function computeFeePayerBalanceStorageSlot(feePayer: AztecAddress) {
|
|
10
|
-
return deriveStorageSlotInMap(
|
|
11
|
+
return deriveStorageSlotInMap(FeeJuiceArtifact.storageLayout.balances.slot, feePayer);
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
/**
|
|
@@ -29,7 +29,7 @@ import { type ContractArtifact, type FunctionArtifact } from '@aztec/foundation/
|
|
|
29
29
|
import { AztecAddress } from '@aztec/foundation/aztec-address';
|
|
30
30
|
import { Fr, Point } from '@aztec/foundation/fields';
|
|
31
31
|
import { openTmpStore } from '@aztec/kv-store/lmdb';
|
|
32
|
-
import { AvmTestContractArtifact } from '@aztec/noir-contracts.js';
|
|
32
|
+
import { AvmTestContractArtifact } from '@aztec/noir-contracts.js/AvmTest';
|
|
33
33
|
import { PublicTxSimulator, WorldStateDB } from '@aztec/simulator';
|
|
34
34
|
import { NoopTelemetryClient } from '@aztec/telemetry-client/noop';
|
|
35
35
|
import { MerkleTrees } from '@aztec/world-state';
|
|
@@ -125,7 +125,7 @@ export function createTxForPublicCall(
|
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
const teardownGasLimits = isTeardown ? gasLimits : Gas.empty();
|
|
128
|
-
const gasSettings = new GasSettings(gasLimits, teardownGasLimits, GasFees.empty());
|
|
128
|
+
const gasSettings = new GasSettings(gasLimits, teardownGasLimits, GasFees.empty(), GasFees.empty());
|
|
129
129
|
const txContext = new TxContext(Fr.zero(), Fr.zero(), gasSettings);
|
|
130
130
|
const constantData = new TxConstantData(BlockHeader.empty(), txContext, Fr.zero(), Fr.zero());
|
|
131
131
|
|
|
@@ -22,7 +22,8 @@ import {
|
|
|
22
22
|
import { computeL1ToL2MessageNullifier, computePublicDataTreeLeafSlot } from '@aztec/circuits.js/hash';
|
|
23
23
|
import { createLogger } from '@aztec/foundation/log';
|
|
24
24
|
import { Timer } from '@aztec/foundation/timer';
|
|
25
|
-
import { ContractClassRegisteredEvent
|
|
25
|
+
import { ContractClassRegisteredEvent } from '@aztec/protocol-contracts/class-registerer';
|
|
26
|
+
import { ContractInstanceDeployedEvent } from '@aztec/protocol-contracts/instance-deployer';
|
|
26
27
|
import {
|
|
27
28
|
type CommitmentsDB,
|
|
28
29
|
MessageLoadOracleInputs,
|
|
@@ -207,7 +208,7 @@ export class WorldStateDB extends ContractsDataSourcePublicDB implements PublicS
|
|
|
207
208
|
nullifier: Fr,
|
|
208
209
|
): Promise<NullifierMembershipWitness | undefined> {
|
|
209
210
|
const timer = new Timer();
|
|
210
|
-
const index = await this.db.
|
|
211
|
+
const index = (await this.db.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, [nullifier.toBuffer()]))[0];
|
|
211
212
|
if (!index) {
|
|
212
213
|
return undefined;
|
|
213
214
|
}
|
|
@@ -240,7 +241,7 @@ export class WorldStateDB extends ContractsDataSourcePublicDB implements PublicS
|
|
|
240
241
|
): Promise<MessageLoadOracleInputs<typeof L1_TO_L2_MSG_TREE_HEIGHT>> {
|
|
241
242
|
const timer = new Timer();
|
|
242
243
|
|
|
243
|
-
const messageIndex = await this.db.
|
|
244
|
+
const messageIndex = (await this.db.findLeafIndices(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, [messageHash]))[0];
|
|
244
245
|
if (messageIndex === undefined) {
|
|
245
246
|
throw new Error(`No L1 to L2 message found for message hash ${messageHash.toString()}`);
|
|
246
247
|
}
|
|
@@ -279,7 +280,7 @@ export class WorldStateDB extends ContractsDataSourcePublicDB implements PublicS
|
|
|
279
280
|
|
|
280
281
|
public async getCommitmentIndex(commitment: Fr): Promise<bigint | undefined> {
|
|
281
282
|
const timer = new Timer();
|
|
282
|
-
const index = await this.db.
|
|
283
|
+
const index = (await this.db.findLeafIndices(MerkleTreeId.NOTE_HASH_TREE, [commitment]))[0];
|
|
283
284
|
this.logger.debug(`[DB] Fetched commitment index`, {
|
|
284
285
|
eventName: 'public-db-access',
|
|
285
286
|
duration: timer.ms(),
|
|
@@ -301,7 +302,7 @@ export class WorldStateDB extends ContractsDataSourcePublicDB implements PublicS
|
|
|
301
302
|
|
|
302
303
|
public async getNullifierIndex(nullifier: Fr): Promise<bigint | undefined> {
|
|
303
304
|
const timer = new Timer();
|
|
304
|
-
const index = await this.db.
|
|
305
|
+
const index = (await this.db.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, [nullifier.toBuffer()]))[0];
|
|
305
306
|
this.logger.debug(`[DB] Fetched nullifier index`, {
|
|
306
307
|
eventName: 'public-db-access',
|
|
307
308
|
duration: timer.ms(),
|
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
type MerkleTreeWriteOperations,
|
|
5
5
|
NestedProcessReturnValues,
|
|
6
6
|
type ProcessedTx,
|
|
7
|
-
type ProcessedTxHandler,
|
|
8
7
|
Tx,
|
|
9
8
|
TxExecutionPhase,
|
|
10
9
|
type TxValidator,
|
|
@@ -25,8 +24,9 @@ import {
|
|
|
25
24
|
import { padArrayEnd } from '@aztec/foundation/collection';
|
|
26
25
|
import { createLogger } from '@aztec/foundation/log';
|
|
27
26
|
import { Timer } from '@aztec/foundation/timer';
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
27
|
+
import { ProtocolContractAddress } from '@aztec/protocol-contracts';
|
|
28
|
+
import { ContractClassRegisteredEvent } from '@aztec/protocol-contracts/class-registerer';
|
|
29
|
+
import { Attributes, type TelemetryClient, type Traceable, type Tracer, trackSpan } from '@aztec/telemetry-client';
|
|
30
30
|
|
|
31
31
|
import { computeFeePayerBalanceLeafSlot, computeFeePayerBalanceStorageSlot } from './fee_payment.js';
|
|
32
32
|
import { WorldStateDB } from './public_db_sources.js';
|
|
@@ -76,7 +76,7 @@ export class PublicProcessorFactory {
|
|
|
76
76
|
* Converts Txs lifted from the P2P module into ProcessedTx objects by executing
|
|
77
77
|
* any public function calls in them. Txs with private calls only are unaffected.
|
|
78
78
|
*/
|
|
79
|
-
export class PublicProcessor {
|
|
79
|
+
export class PublicProcessor implements Traceable {
|
|
80
80
|
private metrics: PublicProcessorMetrics;
|
|
81
81
|
constructor(
|
|
82
82
|
protected db: MerkleTreeWriteOperations,
|
|
@@ -103,7 +103,6 @@ export class PublicProcessor {
|
|
|
103
103
|
public async process(
|
|
104
104
|
txs: Tx[],
|
|
105
105
|
maxTransactions = txs.length,
|
|
106
|
-
processedTxHandler?: ProcessedTxHandler,
|
|
107
106
|
txValidator?: TxValidator<ProcessedTx>,
|
|
108
107
|
): Promise<[ProcessedTx[], FailedTx[], NestedProcessReturnValues[]]> {
|
|
109
108
|
// The processor modifies the tx objects in place, so we need to clone them.
|
|
@@ -118,80 +117,9 @@ export class PublicProcessor {
|
|
|
118
117
|
break;
|
|
119
118
|
}
|
|
120
119
|
try {
|
|
121
|
-
const [processedTx, returnValues] =
|
|
122
|
-
? await this.processPrivateOnlyTx(tx)
|
|
123
|
-
: await this.processTxWithPublicCalls(tx);
|
|
124
|
-
|
|
125
|
-
this.log.verbose(
|
|
126
|
-
!tx.hasPublicCalls()
|
|
127
|
-
? `Processed tx ${processedTx.hash} with no public calls`
|
|
128
|
-
: `Processed tx ${processedTx.hash} with ${tx.enqueuedPublicFunctionCalls.length} public calls`,
|
|
129
|
-
{
|
|
130
|
-
txHash: processedTx.hash,
|
|
131
|
-
txFee: processedTx.txEffect.transactionFee.toBigInt(),
|
|
132
|
-
revertCode: processedTx.txEffect.revertCode.getCode(),
|
|
133
|
-
revertReason: processedTx.revertReason,
|
|
134
|
-
gasUsed: processedTx.gasUsed,
|
|
135
|
-
publicDataWriteCount: processedTx.txEffect.publicDataWrites.length,
|
|
136
|
-
nullifierCount: processedTx.txEffect.nullifiers.length,
|
|
137
|
-
noteHashCount: processedTx.txEffect.noteHashes.length,
|
|
138
|
-
contractClassLogCount: processedTx.txEffect.contractClassLogs.getTotalLogCount(),
|
|
139
|
-
unencryptedLogCount: processedTx.txEffect.unencryptedLogs.getTotalLogCount(),
|
|
140
|
-
privateLogCount: processedTx.txEffect.privateLogs.length,
|
|
141
|
-
l2ToL1MessageCount: processedTx.txEffect.l2ToL1Msgs.length,
|
|
142
|
-
},
|
|
143
|
-
);
|
|
144
|
-
|
|
145
|
-
// Commit the state updates from this transaction
|
|
146
|
-
await this.worldStateDB.commit();
|
|
147
|
-
|
|
148
|
-
// Re-validate the transaction
|
|
149
|
-
if (txValidator) {
|
|
150
|
-
// Only accept processed transactions that are not double-spends,
|
|
151
|
-
// public functions emitting nullifiers would pass earlier check but fail here.
|
|
152
|
-
// Note that we're checking all nullifiers generated in the private execution twice,
|
|
153
|
-
// we could store the ones already checked and skip them here as an optimization.
|
|
154
|
-
const [_, invalid] = await txValidator.validateTxs([processedTx]);
|
|
155
|
-
if (invalid.length) {
|
|
156
|
-
throw new Error(`Transaction ${invalid[0].hash} invalid after processing public functions`);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
// if we were given a handler then send the transaction to it for block building or proving
|
|
160
|
-
if (processedTxHandler) {
|
|
161
|
-
await processedTxHandler.addNewTx(processedTx);
|
|
162
|
-
}
|
|
163
|
-
// Update the state so that the next tx in the loop has the correct .startState
|
|
164
|
-
// NB: before this change, all .startStates were actually incorrect, but the issue was never caught because we either:
|
|
165
|
-
// a) had only 1 tx with public calls per block, so this loop had len 1
|
|
166
|
-
// b) always had a txHandler with the same db passed to it as this.db, which updated the db in buildBaseRollupHints in this loop
|
|
167
|
-
// To see how this ^ happens, move back to one shared db in test_context and run orchestrator_multi_public_functions.test.ts
|
|
168
|
-
// The below is taken from buildBaseRollupHints:
|
|
169
|
-
await this.db.appendLeaves(
|
|
170
|
-
MerkleTreeId.NOTE_HASH_TREE,
|
|
171
|
-
padArrayEnd(processedTx.txEffect.noteHashes, Fr.ZERO, MAX_NOTE_HASHES_PER_TX),
|
|
172
|
-
);
|
|
173
|
-
try {
|
|
174
|
-
await this.db.batchInsert(
|
|
175
|
-
MerkleTreeId.NULLIFIER_TREE,
|
|
176
|
-
padArrayEnd(processedTx.txEffect.nullifiers, Fr.ZERO, MAX_NULLIFIERS_PER_TX).map(n => n.toBuffer()),
|
|
177
|
-
NULLIFIER_SUBTREE_HEIGHT,
|
|
178
|
-
);
|
|
179
|
-
} catch (error) {
|
|
180
|
-
if (txValidator) {
|
|
181
|
-
// Ideally the validator has already caught this above, but just in case:
|
|
182
|
-
throw new Error(`Transaction ${processedTx.hash} invalid after processing public functions`);
|
|
183
|
-
} else {
|
|
184
|
-
// We have no validator and assume this call should blindly process txs with duplicates being caught later
|
|
185
|
-
this.log.warn(`Detected duplicate nullifier after public processing for: ${processedTx.hash}.`);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
await this.db.sequentialInsert(
|
|
190
|
-
MerkleTreeId.PUBLIC_DATA_TREE,
|
|
191
|
-
processedTx.txEffect.publicDataWrites.map(x => x.toBuffer()),
|
|
192
|
-
);
|
|
120
|
+
const [processedTx, returnValues] = await this.processTx(tx, txValidator);
|
|
193
121
|
result.push(processedTx);
|
|
194
|
-
returns = returns.concat(returnValues
|
|
122
|
+
returns = returns.concat(returnValues);
|
|
195
123
|
} catch (err: any) {
|
|
196
124
|
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
|
|
197
125
|
this.log.warn(`Failed to process tx ${tx.getTxHash()}: ${errorMessage} ${err?.stack}`);
|
|
@@ -207,6 +135,83 @@ export class PublicProcessor {
|
|
|
207
135
|
return [result, failed, returns];
|
|
208
136
|
}
|
|
209
137
|
|
|
138
|
+
@trackSpan('PublicProcessor.processTx', tx => ({ [Attributes.TX_HASH]: tx.tryGetTxHash()?.toString() }))
|
|
139
|
+
private async processTx(
|
|
140
|
+
tx: Tx,
|
|
141
|
+
txValidator?: TxValidator<ProcessedTx>,
|
|
142
|
+
): Promise<[ProcessedTx, NestedProcessReturnValues[]]> {
|
|
143
|
+
const [processedTx, returnValues] = !tx.hasPublicCalls()
|
|
144
|
+
? await this.processPrivateOnlyTx(tx)
|
|
145
|
+
: await this.processTxWithPublicCalls(tx);
|
|
146
|
+
|
|
147
|
+
this.log.verbose(
|
|
148
|
+
!tx.hasPublicCalls()
|
|
149
|
+
? `Processed tx ${processedTx.hash} with no public calls`
|
|
150
|
+
: `Processed tx ${processedTx.hash} with ${tx.enqueuedPublicFunctionCalls.length} public calls`,
|
|
151
|
+
{
|
|
152
|
+
txHash: processedTx.hash,
|
|
153
|
+
txFee: processedTx.txEffect.transactionFee.toBigInt(),
|
|
154
|
+
revertCode: processedTx.txEffect.revertCode.getCode(),
|
|
155
|
+
revertReason: processedTx.revertReason,
|
|
156
|
+
gasUsed: processedTx.gasUsed,
|
|
157
|
+
publicDataWriteCount: processedTx.txEffect.publicDataWrites.length,
|
|
158
|
+
nullifierCount: processedTx.txEffect.nullifiers.length,
|
|
159
|
+
noteHashCount: processedTx.txEffect.noteHashes.length,
|
|
160
|
+
contractClassLogCount: processedTx.txEffect.contractClassLogs.getTotalLogCount(),
|
|
161
|
+
unencryptedLogCount: processedTx.txEffect.unencryptedLogs.getTotalLogCount(),
|
|
162
|
+
privateLogCount: processedTx.txEffect.privateLogs.length,
|
|
163
|
+
l2ToL1MessageCount: processedTx.txEffect.l2ToL1Msgs.length,
|
|
164
|
+
},
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
// Commit the state updates from this transaction
|
|
168
|
+
await this.worldStateDB.commit();
|
|
169
|
+
|
|
170
|
+
// Re-validate the transaction
|
|
171
|
+
if (txValidator) {
|
|
172
|
+
// Only accept processed transactions that are not double-spends,
|
|
173
|
+
// public functions emitting nullifiers would pass earlier check but fail here.
|
|
174
|
+
// Note that we're checking all nullifiers generated in the private execution twice,
|
|
175
|
+
// we could store the ones already checked and skip them here as an optimization.
|
|
176
|
+
const [_, invalid] = await txValidator.validateTxs([processedTx]);
|
|
177
|
+
if (invalid.length) {
|
|
178
|
+
throw new Error(`Transaction ${invalid[0].hash} invalid after processing public functions`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// Update the state so that the next tx in the loop has the correct .startState
|
|
182
|
+
// NB: before this change, all .startStates were actually incorrect, but the issue was never caught because we either:
|
|
183
|
+
// a) had only 1 tx with public calls per block, so this loop had len 1
|
|
184
|
+
// b) always had a txHandler with the same db passed to it as this.db, which updated the db in buildBaseRollupHints in this loop
|
|
185
|
+
// To see how this ^ happens, move back to one shared db in test_context and run orchestrator_multi_public_functions.test.ts
|
|
186
|
+
// The below is taken from buildBaseRollupHints:
|
|
187
|
+
await this.db.appendLeaves(
|
|
188
|
+
MerkleTreeId.NOTE_HASH_TREE,
|
|
189
|
+
padArrayEnd(processedTx.txEffect.noteHashes, Fr.ZERO, MAX_NOTE_HASHES_PER_TX),
|
|
190
|
+
);
|
|
191
|
+
try {
|
|
192
|
+
await this.db.batchInsert(
|
|
193
|
+
MerkleTreeId.NULLIFIER_TREE,
|
|
194
|
+
padArrayEnd(processedTx.txEffect.nullifiers, Fr.ZERO, MAX_NULLIFIERS_PER_TX).map(n => n.toBuffer()),
|
|
195
|
+
NULLIFIER_SUBTREE_HEIGHT,
|
|
196
|
+
);
|
|
197
|
+
} catch (error) {
|
|
198
|
+
if (txValidator) {
|
|
199
|
+
// Ideally the validator has already caught this above, but just in case:
|
|
200
|
+
throw new Error(`Transaction ${processedTx.hash} invalid after processing public functions`);
|
|
201
|
+
} else {
|
|
202
|
+
// We have no validator and assume this call should blindly process txs with duplicates being caught later
|
|
203
|
+
this.log.warn(`Detected duplicate nullifier after public processing for: ${processedTx.hash}.`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
await this.db.sequentialInsert(
|
|
208
|
+
MerkleTreeId.PUBLIC_DATA_TREE,
|
|
209
|
+
processedTx.txEffect.publicDataWrites.map(x => x.toBuffer()),
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
return [processedTx, returnValues ?? []];
|
|
213
|
+
}
|
|
214
|
+
|
|
210
215
|
/**
|
|
211
216
|
* Creates the public data write for paying the tx fee.
|
|
212
217
|
* This is used in private only txs, since for txs with public calls
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type TxExecutionPhase } from '@aztec/circuit-types';
|
|
2
|
-
import { type ContractClassRegisteredEvent } from '@aztec/protocol-contracts';
|
|
2
|
+
import { type ContractClassRegisteredEvent } from '@aztec/protocol-contracts/class-registerer';
|
|
3
3
|
import {
|
|
4
4
|
Attributes,
|
|
5
5
|
type Histogram,
|
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
TxHash,
|
|
11
11
|
} from '@aztec/circuit-types';
|
|
12
12
|
import {
|
|
13
|
-
AppendOnlyTreeSnapshot,
|
|
14
13
|
AvmCircuitInputs,
|
|
15
14
|
type AvmCircuitPublicInputs,
|
|
16
15
|
type AztecAddress,
|
|
@@ -19,12 +18,15 @@ import {
|
|
|
19
18
|
type GasSettings,
|
|
20
19
|
type GlobalVariables,
|
|
21
20
|
MAX_L2_GAS_PER_TX_PUBLIC_PORTION,
|
|
21
|
+
MAX_NOTE_HASHES_PER_TX,
|
|
22
|
+
MAX_NULLIFIERS_PER_TX,
|
|
22
23
|
type PrivateToPublicAccumulatedData,
|
|
23
24
|
type PublicCallRequest,
|
|
24
25
|
PublicCircuitPublicInputs,
|
|
25
26
|
RevertCode,
|
|
26
27
|
type StateReference,
|
|
27
28
|
TreeSnapshots,
|
|
29
|
+
computeTransactionFee,
|
|
28
30
|
countAccumulatedItems,
|
|
29
31
|
} from '@aztec/circuits.js';
|
|
30
32
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
@@ -91,7 +93,7 @@ export class PublicTxContext {
|
|
|
91
93
|
const previousAccumulatedDataArrayLengths = new SideEffectArrayLengths(
|
|
92
94
|
/*publicDataWrites*/ 0,
|
|
93
95
|
/*protocolPublicDataWrites*/ 0,
|
|
94
|
-
|
|
96
|
+
/*noteHashes*/ 0,
|
|
95
97
|
/*nullifiers=*/ 0,
|
|
96
98
|
countAccumulatedItems(nonRevertibleAccumulatedDataFromPrivate.l2ToL1Msgs),
|
|
97
99
|
/*unencryptedLogsHashes*/ 0,
|
|
@@ -102,7 +104,12 @@ export class PublicTxContext {
|
|
|
102
104
|
);
|
|
103
105
|
|
|
104
106
|
// Transaction level state manager that will be forked for revertible phases.
|
|
105
|
-
const txStateManager = await AvmPersistableStateManager.create(
|
|
107
|
+
const txStateManager = await AvmPersistableStateManager.create(
|
|
108
|
+
worldStateDB,
|
|
109
|
+
enqueuedCallTrace,
|
|
110
|
+
doMerkleOperations,
|
|
111
|
+
fetchTxHash(nonRevertibleAccumulatedDataFromPrivate),
|
|
112
|
+
);
|
|
106
113
|
|
|
107
114
|
const gasSettings = tx.data.constants.txContext.gasSettings;
|
|
108
115
|
const gasUsedByPrivate = tx.data.gasUsed;
|
|
@@ -186,12 +193,7 @@ export class PublicTxContext {
|
|
|
186
193
|
* @returns The transaction's hash.
|
|
187
194
|
*/
|
|
188
195
|
getTxHash(): TxHash {
|
|
189
|
-
|
|
190
|
-
const firstNullifier = this.nonRevertibleAccumulatedDataFromPrivate.nullifiers[0];
|
|
191
|
-
if (!firstNullifier || firstNullifier.isZero()) {
|
|
192
|
-
throw new Error(`Cannot get tx hash since first nullifier is missing`);
|
|
193
|
-
}
|
|
194
|
-
return new TxHash(firstNullifier.toBuffer());
|
|
196
|
+
return fetchTxHash(this.nonRevertibleAccumulatedDataFromPrivate);
|
|
195
197
|
}
|
|
196
198
|
|
|
197
199
|
/**
|
|
@@ -297,12 +299,15 @@ export class PublicTxContext {
|
|
|
297
299
|
* Should only be called during or after teardown.
|
|
298
300
|
*/
|
|
299
301
|
private getTransactionFeeUnsafe(): Fr {
|
|
300
|
-
const
|
|
302
|
+
const gasUsed = this.getTotalGasUsed();
|
|
303
|
+
const txFee = computeTransactionFee(this.globalVariables.gasFees, this.gasSettings, gasUsed);
|
|
304
|
+
|
|
301
305
|
this.log.debug(`Computed tx fee`, {
|
|
302
306
|
txFee,
|
|
303
|
-
gasUsed: inspect(
|
|
307
|
+
gasUsed: inspect(gasUsed),
|
|
304
308
|
gasFees: inspect(this.globalVariables.gasFees),
|
|
305
309
|
});
|
|
310
|
+
|
|
306
311
|
return txFee;
|
|
307
312
|
}
|
|
308
313
|
|
|
@@ -311,16 +316,29 @@ export class PublicTxContext {
|
|
|
311
316
|
*/
|
|
312
317
|
private generateAvmCircuitPublicInputs(endStateReference: StateReference): AvmCircuitPublicInputs {
|
|
313
318
|
assert(this.halted, 'Can only get AvmCircuitPublicInputs after tx execution ends');
|
|
314
|
-
const ephemeralTrees = this.state.getActiveStateManager().merkleTrees
|
|
315
|
-
|
|
316
|
-
const
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
319
|
+
const ephemeralTrees = this.state.getActiveStateManager().merkleTrees;
|
|
320
|
+
|
|
321
|
+
const noteHashTree = ephemeralTrees.getTreeSnapshot(MerkleTreeId.NOTE_HASH_TREE);
|
|
322
|
+
const nullifierTree = ephemeralTrees.getTreeSnapshot(MerkleTreeId.NULLIFIER_TREE);
|
|
323
|
+
const publicDataTree = ephemeralTrees.getTreeSnapshot(MerkleTreeId.PUBLIC_DATA_TREE);
|
|
324
|
+
// Pad the note hash and nullifier trees
|
|
325
|
+
const paddedNoteHashTreeSize =
|
|
326
|
+
this.startStateReference.partial.noteHashTree.nextAvailableLeafIndex + MAX_NOTE_HASHES_PER_TX;
|
|
327
|
+
if (noteHashTree.nextAvailableLeafIndex > paddedNoteHashTreeSize) {
|
|
328
|
+
throw new Error(
|
|
329
|
+
`Inserted too many leaves in note hash tree: ${noteHashTree.nextAvailableLeafIndex} > ${paddedNoteHashTreeSize}`,
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
noteHashTree.nextAvailableLeafIndex = paddedNoteHashTreeSize;
|
|
333
|
+
|
|
334
|
+
const paddedNullifierTreeSize =
|
|
335
|
+
this.startStateReference.partial.nullifierTree.nextAvailableLeafIndex + MAX_NULLIFIERS_PER_TX;
|
|
336
|
+
if (nullifierTree.nextAvailableLeafIndex > paddedNullifierTreeSize) {
|
|
337
|
+
throw new Error(
|
|
338
|
+
`Inserted too many leaves in nullifier tree: ${nullifierTree.nextAvailableLeafIndex} > ${paddedNullifierTreeSize}`,
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
nullifierTree.nextAvailableLeafIndex = paddedNullifierTreeSize;
|
|
324
342
|
|
|
325
343
|
const endTreeSnapshots = new TreeSnapshots(
|
|
326
344
|
endStateReference.l1ToL2MessageTree,
|
|
@@ -425,3 +443,12 @@ function applyMaxToAvailableGas(availableGas: Gas) {
|
|
|
425
443
|
/*l2Gas=*/ Math.min(availableGas.l2Gas, MAX_L2_GAS_PER_TX_PUBLIC_PORTION),
|
|
426
444
|
);
|
|
427
445
|
}
|
|
446
|
+
|
|
447
|
+
function fetchTxHash(nonRevertibleAccumulatedData: PrivateToPublicAccumulatedData): TxHash {
|
|
448
|
+
// Private kernel functions are executed client side and for this reason tx hash is already set as first nullifier
|
|
449
|
+
const firstNullifier = nonRevertibleAccumulatedData.nullifiers[0];
|
|
450
|
+
if (!firstNullifier || firstNullifier.isZero()) {
|
|
451
|
+
throw new Error(`Cannot get tx hash since first nullifier is missing`);
|
|
452
|
+
}
|
|
453
|
+
return new TxHash(firstNullifier.toBuffer());
|
|
454
|
+
}
|
|
@@ -378,6 +378,11 @@ export class PublicTxSimulator {
|
|
|
378
378
|
);
|
|
379
379
|
}
|
|
380
380
|
}
|
|
381
|
+
for (const noteHash of context.nonRevertibleAccumulatedDataFromPrivate.noteHashes) {
|
|
382
|
+
if (!noteHash.isEmpty()) {
|
|
383
|
+
stateManager.writeUniqueNoteHash(noteHash);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
381
386
|
}
|
|
382
387
|
|
|
383
388
|
/**
|
|
@@ -397,6 +402,12 @@ export class PublicTxSimulator {
|
|
|
397
402
|
);
|
|
398
403
|
}
|
|
399
404
|
}
|
|
405
|
+
for (const noteHash of context.revertibleAccumulatedDataFromPrivate.noteHashes) {
|
|
406
|
+
if (!noteHash.isEmpty()) {
|
|
407
|
+
// Revertible note hashes from private are not hashed with nonce, since private can't know their final position, only we can.
|
|
408
|
+
stateManager.writeSiloedNoteHash(noteHash);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
400
411
|
}
|
|
401
412
|
|
|
402
413
|
private async payFee(context: PublicTxContext) {
|
|
@@ -39,7 +39,8 @@ export interface PublicSideEffectTraceInterface {
|
|
|
39
39
|
insertionPath?: Fr[],
|
|
40
40
|
): void;
|
|
41
41
|
traceNoteHashCheck(contractAddress: AztecAddress, noteHash: Fr, leafIndex: Fr, exists: boolean, path?: Fr[]): void;
|
|
42
|
-
traceNewNoteHash(
|
|
42
|
+
traceNewNoteHash(uniqueNoteHash: Fr, leafIndex?: Fr, path?: Fr[]): void;
|
|
43
|
+
getNoteHashCount(): number;
|
|
43
44
|
traceNullifierCheck(
|
|
44
45
|
siloedNullifier: Fr,
|
|
45
46
|
exists: boolean,
|
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
type GasSettings,
|
|
7
7
|
type GlobalVariables,
|
|
8
8
|
MAX_L2_TO_L1_MSGS_PER_TX,
|
|
9
|
-
MAX_NOTE_HASHES_PER_TX,
|
|
10
9
|
MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX,
|
|
11
10
|
PrivateToAvmAccumulatedData,
|
|
12
11
|
PrivateToAvmAccumulatedDataArrayLengths,
|
|
@@ -19,7 +18,6 @@ import {
|
|
|
19
18
|
countAccumulatedItems,
|
|
20
19
|
mergeAccumulatedData,
|
|
21
20
|
} from '@aztec/circuits.js';
|
|
22
|
-
import { computeNoteHashNonce, computeUniqueNoteHash, siloNoteHash } from '@aztec/circuits.js/hash';
|
|
23
21
|
import { padArrayEnd } from '@aztec/foundation/collection';
|
|
24
22
|
import { assertLength } from '@aztec/foundation/serialize';
|
|
25
23
|
|
|
@@ -86,55 +84,6 @@ export function generateAvmCircuitPublicInputs(
|
|
|
86
84
|
revertibleAccumulatedDataFromPrivate,
|
|
87
85
|
);
|
|
88
86
|
|
|
89
|
-
const txHash = avmCircuitPublicInputs.previousNonRevertibleAccumulatedData.nullifiers[0];
|
|
90
|
-
|
|
91
|
-
// Add nonces to revertible note hashes from private. These don't have nonces since we don't know
|
|
92
|
-
// the final position in the tx until the AVM has executed.
|
|
93
|
-
// TODO: Use the final position in the tx
|
|
94
|
-
for (
|
|
95
|
-
let revertibleIndex = 0;
|
|
96
|
-
revertibleIndex < avmCircuitPublicInputs.previousRevertibleAccumulatedData.noteHashes.length;
|
|
97
|
-
revertibleIndex++
|
|
98
|
-
) {
|
|
99
|
-
const noteHash = avmCircuitPublicInputs.previousRevertibleAccumulatedData.noteHashes[revertibleIndex];
|
|
100
|
-
if (noteHash.isZero()) {
|
|
101
|
-
continue;
|
|
102
|
-
}
|
|
103
|
-
const indexInTx =
|
|
104
|
-
revertibleIndex + avmCircuitPublicInputs.previousNonRevertibleAccumulatedDataArrayLengths.noteHashes;
|
|
105
|
-
|
|
106
|
-
const nonce = computeNoteHashNonce(txHash, indexInTx);
|
|
107
|
-
const uniqueNoteHash = computeUniqueNoteHash(nonce, noteHash);
|
|
108
|
-
avmCircuitPublicInputs.previousRevertibleAccumulatedData.noteHashes[revertibleIndex] = uniqueNoteHash;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// merge all revertible & non-revertible side effects into output accumulated data
|
|
112
|
-
const noteHashesFromPrivate = revertCode.isOK()
|
|
113
|
-
? mergeAccumulatedData(
|
|
114
|
-
avmCircuitPublicInputs.previousNonRevertibleAccumulatedData.noteHashes,
|
|
115
|
-
avmCircuitPublicInputs.previousRevertibleAccumulatedData.noteHashes,
|
|
116
|
-
)
|
|
117
|
-
: avmCircuitPublicInputs.previousNonRevertibleAccumulatedData.noteHashes;
|
|
118
|
-
avmCircuitPublicInputs.accumulatedData.noteHashes = assertLength(
|
|
119
|
-
mergeAccumulatedData(noteHashesFromPrivate, avmCircuitPublicInputs.accumulatedData.noteHashes),
|
|
120
|
-
MAX_NOTE_HASHES_PER_TX,
|
|
121
|
-
);
|
|
122
|
-
|
|
123
|
-
// Silo and add nonces for note hashes emitted by the AVM
|
|
124
|
-
const scopedNoteHashesFromPublic = trace.getSideEffects().noteHashes;
|
|
125
|
-
for (let i = 0; i < scopedNoteHashesFromPublic.length; i++) {
|
|
126
|
-
const scopedNoteHash = scopedNoteHashesFromPublic[i];
|
|
127
|
-
const noteHash = scopedNoteHash.value;
|
|
128
|
-
if (!noteHash.isZero()) {
|
|
129
|
-
const noteHashIndexInTx = i + countAccumulatedItems(noteHashesFromPrivate);
|
|
130
|
-
const nonce = computeNoteHashNonce(txHash, noteHashIndexInTx);
|
|
131
|
-
const siloedNoteHash = siloNoteHash(scopedNoteHash.contractAddress, noteHash);
|
|
132
|
-
const uniqueNoteHash = computeUniqueNoteHash(nonce, siloedNoteHash);
|
|
133
|
-
|
|
134
|
-
avmCircuitPublicInputs.accumulatedData.noteHashes[noteHashIndexInTx] = uniqueNoteHash;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
87
|
const msgsFromPrivate = revertCode.isOK()
|
|
139
88
|
? mergeAccumulatedData(
|
|
140
89
|
avmCircuitPublicInputs.previousNonRevertibleAccumulatedData.l2ToL1Msgs,
|