@aztec/txe 0.0.1-commit.6230efd → 0.0.1-commit.6a729f7

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 (52) hide show
  1. package/dest/oracle/interfaces.d.ts +3 -3
  2. package/dest/oracle/interfaces.d.ts.map +1 -1
  3. package/dest/oracle/txe_oracle_public_context.d.ts +3 -3
  4. package/dest/oracle/txe_oracle_public_context.d.ts.map +1 -1
  5. package/dest/oracle/txe_oracle_public_context.js +6 -6
  6. package/dest/oracle/txe_oracle_top_level_context.d.ts +3 -2
  7. package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
  8. package/dest/oracle/txe_oracle_top_level_context.js +29 -21
  9. package/dest/rpc_translator.d.ts +16 -10
  10. package/dest/rpc_translator.d.ts.map +1 -1
  11. package/dest/rpc_translator.js +53 -40
  12. package/dest/state_machine/archiver.d.ts +20 -67
  13. package/dest/state_machine/archiver.d.ts.map +1 -1
  14. package/dest/state_machine/archiver.js +57 -178
  15. package/dest/state_machine/dummy_p2p_client.d.ts +8 -7
  16. package/dest/state_machine/dummy_p2p_client.d.ts.map +1 -1
  17. package/dest/state_machine/dummy_p2p_client.js +13 -10
  18. package/dest/state_machine/global_variable_builder.d.ts +2 -2
  19. package/dest/state_machine/global_variable_builder.d.ts.map +1 -1
  20. package/dest/state_machine/global_variable_builder.js +1 -1
  21. package/dest/state_machine/index.d.ts +1 -1
  22. package/dest/state_machine/index.d.ts.map +1 -1
  23. package/dest/state_machine/index.js +22 -4
  24. package/dest/state_machine/mock_epoch_cache.d.ts +7 -6
  25. package/dest/state_machine/mock_epoch_cache.d.ts.map +1 -1
  26. package/dest/state_machine/mock_epoch_cache.js +10 -7
  27. package/dest/state_machine/synchronizer.d.ts +3 -3
  28. package/dest/state_machine/synchronizer.d.ts.map +1 -1
  29. package/dest/txe_session.d.ts +5 -3
  30. package/dest/txe_session.d.ts.map +1 -1
  31. package/dest/txe_session.js +30 -14
  32. package/dest/util/encoding.d.ts +17 -17
  33. package/dest/utils/block_creation.d.ts +4 -4
  34. package/dest/utils/block_creation.d.ts.map +1 -1
  35. package/dest/utils/block_creation.js +16 -5
  36. package/dest/utils/tx_effect_creation.d.ts +2 -3
  37. package/dest/utils/tx_effect_creation.d.ts.map +1 -1
  38. package/dest/utils/tx_effect_creation.js +3 -6
  39. package/package.json +16 -16
  40. package/src/oracle/interfaces.ts +2 -2
  41. package/src/oracle/txe_oracle_public_context.ts +6 -8
  42. package/src/oracle/txe_oracle_top_level_context.ts +49 -21
  43. package/src/rpc_translator.ts +62 -47
  44. package/src/state_machine/archiver.ts +52 -220
  45. package/src/state_machine/dummy_p2p_client.ts +18 -13
  46. package/src/state_machine/global_variable_builder.ts +1 -1
  47. package/src/state_machine/index.ts +26 -4
  48. package/src/state_machine/mock_epoch_cache.ts +10 -11
  49. package/src/state_machine/synchronizer.ts +2 -2
  50. package/src/txe_session.ts +37 -20
  51. package/src/utils/block_creation.ts +17 -16
  52. package/src/utils/tx_effect_creation.ts +3 -11
@@ -1,11 +1,14 @@
1
1
  import { type AztecNodeConfig, AztecNodeService } from '@aztec/aztec-node';
2
2
  import { TestCircuitVerifier } from '@aztec/bb-prover/test';
3
+ import { CheckpointNumber } from '@aztec/foundation/branded-types';
4
+ import { Fr } from '@aztec/foundation/curves/bn254';
3
5
  import { createLogger } from '@aztec/foundation/log';
4
6
  import type { AztecAsyncKVStore } from '@aztec/kv-store';
5
7
  import { AnchorBlockStore } from '@aztec/pxe/server';
6
8
  import { L2Block } from '@aztec/stdlib/block';
7
- import { L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
9
+ import { Checkpoint, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
8
10
  import type { AztecNode } from '@aztec/stdlib/interfaces/client';
11
+ import { CheckpointHeader } from '@aztec/stdlib/rollup';
9
12
  import { getPackageVersion } from '@aztec/stdlib/update-checker';
10
13
 
11
14
  import { TXEArchiver } from './archiver.js';
@@ -59,7 +62,26 @@ export class TXEStateMachine {
59
62
  }
60
63
 
61
64
  public async handleL2Block(block: L2Block) {
62
- const checkpoint = block.toCheckpoint();
65
+ // Create a checkpoint from the block manually
66
+ const checkpoint = new Checkpoint(
67
+ block.archive,
68
+ CheckpointHeader.from({
69
+ lastArchiveRoot: block.header.lastArchive.root,
70
+ inHash: Fr.ZERO,
71
+ blobsHash: Fr.ZERO,
72
+ blockHeadersHash: Fr.ZERO,
73
+ epochOutHash: Fr.ZERO,
74
+ slotNumber: block.header.globalVariables.slotNumber,
75
+ timestamp: block.header.globalVariables.timestamp,
76
+ coinbase: block.header.globalVariables.coinbase,
77
+ feeRecipient: block.header.globalVariables.feeRecipient,
78
+ gasFees: block.header.globalVariables.gasFees,
79
+ totalManaUsed: block.header.totalManaUsed,
80
+ }),
81
+ [block],
82
+ CheckpointNumber.fromBlockNumber(block.number),
83
+ );
84
+
63
85
  const publishedCheckpoint = new PublishedCheckpoint(
64
86
  checkpoint,
65
87
  new L1PublishedData(
@@ -70,9 +92,9 @@ export class TXEStateMachine {
70
92
  [],
71
93
  );
72
94
  await Promise.all([
73
- this.synchronizer.handleL2Block(block.toL2Block()),
95
+ this.synchronizer.handleL2Block(block),
74
96
  this.archiver.addCheckpoints([publishedCheckpoint], undefined),
75
- this.anchorBlockStore.setHeader(block.getBlockHeader()),
97
+ this.anchorBlockStore.setHeader(block.header),
76
98
  ]);
77
99
  }
78
100
  }
@@ -12,14 +12,16 @@ export class MockEpochCache implements EpochCacheInterface {
12
12
  committee: undefined,
13
13
  seed: 0n,
14
14
  epoch: EpochNumber.ZERO,
15
+ isEscapeHatchOpen: false,
15
16
  });
16
17
  }
17
18
 
18
- getEpochAndSlotNow(): EpochAndSlot {
19
+ getEpochAndSlotNow(): EpochAndSlot & { nowMs: bigint } {
19
20
  return {
20
21
  epoch: EpochNumber.ZERO,
21
22
  slot: SlotNumber(0),
22
23
  ts: 0n,
24
+ nowMs: 0n,
23
25
  };
24
26
  }
25
27
 
@@ -40,18 +42,15 @@ export class MockEpochCache implements EpochCacheInterface {
40
42
  return 0n;
41
43
  }
42
44
 
43
- getProposerAttesterAddressInCurrentOrNextSlot(): Promise<{
44
- currentProposer: EthAddress | undefined;
45
- nextProposer: EthAddress | undefined;
46
- currentSlot: SlotNumber;
47
- nextSlot: SlotNumber;
48
- }> {
49
- return Promise.resolve({
50
- currentProposer: undefined,
51
- nextProposer: undefined,
45
+ getCurrentAndNextSlot(): { currentSlot: SlotNumber; nextSlot: SlotNumber } {
46
+ return {
52
47
  currentSlot: SlotNumber(0),
53
48
  nextSlot: SlotNumber(0),
54
- });
49
+ };
50
+ }
51
+
52
+ getProposerAttesterAddressInSlot(_slot: SlotNumber): Promise<EthAddress | undefined> {
53
+ return Promise.resolve(undefined);
55
54
  }
56
55
 
57
56
  isInCommittee(_slot: SlotTag, _validator: EthAddress): Promise<boolean> {
@@ -1,7 +1,7 @@
1
1
  import { NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/constants';
2
2
  import { BlockNumber } from '@aztec/foundation/branded-types';
3
3
  import { Fr } from '@aztec/foundation/curves/bn254';
4
- import type { L2BlockNew } from '@aztec/stdlib/block';
4
+ import type { L2Block } from '@aztec/stdlib/block';
5
5
  import type {
6
6
  MerkleTreeReadOperations,
7
7
  MerkleTreeWriteOperations,
@@ -23,7 +23,7 @@ export class TXESynchronizer implements WorldStateSynchronizer {
23
23
  return new this(nativeWorldStateService);
24
24
  }
25
25
 
26
- public async handleL2Block(block: L2BlockNew) {
26
+ public async handleL2Block(block: L2Block) {
27
27
  await this.nativeWorldStateService.handleL2BlockAndMessages(
28
28
  block,
29
29
  Array(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).fill(0).map(Fr.zero),
@@ -7,6 +7,7 @@ import type { ProtocolContract } from '@aztec/protocol-contracts';
7
7
  import {
8
8
  AddressStore,
9
9
  CapsuleStore,
10
+ JobCoordinator,
10
11
  NoteService,
11
12
  NoteStore,
12
13
  PrivateEventStore,
@@ -74,7 +75,6 @@ type SessionState =
74
75
  | {
75
76
  name: 'PRIVATE';
76
77
  nextBlockGlobalVariables: GlobalVariables;
77
- protocolNullifier: Fr;
78
78
  noteCache: ExecutionNoteCache;
79
79
  taggingIndexCache: ExecutionTaggingIndexCache;
80
80
  }
@@ -139,6 +139,8 @@ export class TXESession implements TXESessionStateHandler {
139
139
  private senderAddressBookStore: SenderAddressBookStore,
140
140
  private capsuleStore: CapsuleStore,
141
141
  private privateEventStore: PrivateEventStore,
142
+ private jobCoordinator: JobCoordinator,
143
+ private currentJobId: string,
142
144
  private chainId: Fr,
143
145
  private version: Fr,
144
146
  private nextBlockTimestamp: bigint,
@@ -150,7 +152,7 @@ export class TXESession implements TXESessionStateHandler {
150
152
  const addressStore = new AddressStore(store);
151
153
  const privateEventStore = new PrivateEventStore(store);
152
154
  const contractStore = new TXEContractStore(store);
153
- const noteStore = await NoteStore.create(store);
155
+ const noteStore = new NoteStore(store);
154
156
  const senderTaggingStore = new SenderTaggingStore(store);
155
157
  const recipientTaggingStore = new RecipientTaggingStore(store);
156
158
  const senderAddressBookStore = new SenderAddressBookStore(store);
@@ -158,6 +160,16 @@ export class TXESession implements TXESessionStateHandler {
158
160
  const keyStore = new KeyStore(store);
159
161
  const accountStore = new TXEAccountStore(store);
160
162
 
163
+ // Create job coordinator and register staged stores
164
+ const jobCoordinator = new JobCoordinator(store);
165
+ jobCoordinator.registerStores([
166
+ capsuleStore,
167
+ senderTaggingStore,
168
+ recipientTaggingStore,
169
+ privateEventStore,
170
+ noteStore,
171
+ ]);
172
+
161
173
  // Register protocol contracts.
162
174
  for (const { contractClass, instance, artifact } of protocolContracts) {
163
175
  await contractStore.addContractArtifact(contractClass.id, artifact);
@@ -170,6 +182,8 @@ export class TXESession implements TXESessionStateHandler {
170
182
  const version = new Fr(await stateMachine.node.getVersion());
171
183
  const chainId = new Fr(await stateMachine.node.getChainId());
172
184
 
185
+ const initialJobId = jobCoordinator.beginJob();
186
+
173
187
  const topLevelOracleHandler = new TXEOracleTopLevelContext(
174
188
  stateMachine,
175
189
  contractStore,
@@ -182,6 +196,7 @@ export class TXESession implements TXESessionStateHandler {
182
196
  senderAddressBookStore,
183
197
  capsuleStore,
184
198
  privateEventStore,
199
+ initialJobId,
185
200
  nextBlockTimestamp,
186
201
  version,
187
202
  chainId,
@@ -203,6 +218,8 @@ export class TXESession implements TXESessionStateHandler {
203
218
  senderAddressBookStore,
204
219
  capsuleStore,
205
220
  privateEventStore,
221
+ jobCoordinator,
222
+ initialJobId,
206
223
  version,
207
224
  chainId,
208
225
  nextBlockTimestamp,
@@ -262,6 +279,10 @@ export class TXESession implements TXESessionStateHandler {
262
279
  }
263
280
  }
264
281
 
282
+ // Commit all staged stores from the job that was just completed, then begin a new job
283
+ await this.jobCoordinator.commitJob(this.currentJobId);
284
+ this.currentJobId = this.jobCoordinator.beginJob();
285
+
265
286
  this.oracleHandler = new TXEOracleTopLevelContext(
266
287
  this.stateMachine,
267
288
  this.contractStore,
@@ -274,6 +295,7 @@ export class TXESession implements TXESessionStateHandler {
274
295
  this.senderAddressBookStore,
275
296
  this.capsuleStore,
276
297
  this.privateEventStore,
298
+ this.currentJobId,
277
299
  this.nextBlockTimestamp,
278
300
  this.version,
279
301
  this.chainId,
@@ -290,16 +312,14 @@ export class TXESession implements TXESessionStateHandler {
290
312
  ): Promise<PrivateContextInputs> {
291
313
  this.exitTopLevelState();
292
314
 
293
- await new NoteService(
294
- this.noteStore,
295
- this.stateMachine.node,
296
- this.stateMachine.anchorBlockStore,
297
- ).syncNoteNullifiers(contractAddress);
298
-
299
315
  // Private execution has two associated block numbers: the anchor block (i.e. the historical block that is used to
300
316
  // build the proof), and the *next* block, i.e. the one we'll create once the execution ends, and which will contain
301
317
  // a single transaction with the effects of what was done in the test.
302
318
  const anchorBlock = await this.stateMachine.node.getBlockHeader(anchorBlockNumber ?? 'latest');
319
+
320
+ await new NoteService(this.noteStore, this.stateMachine.node, anchorBlock!, this.currentJobId).syncNoteNullifiers(
321
+ contractAddress,
322
+ );
303
323
  const latestBlock = await this.stateMachine.node.getBlockHeader('latest');
304
324
 
305
325
  const nextBlockGlobalVariables = makeGlobalVariables(undefined, {
@@ -331,12 +351,12 @@ export class TXESession implements TXESessionStateHandler {
331
351
  this.keyStore,
332
352
  this.addressStore,
333
353
  this.stateMachine.node,
334
- this.stateMachine.anchorBlockStore,
335
354
  this.senderTaggingStore,
336
355
  this.recipientTaggingStore,
337
356
  this.senderAddressBookStore,
338
357
  this.capsuleStore,
339
358
  this.privateEventStore,
359
+ this.currentJobId,
340
360
  );
341
361
 
342
362
  // We store the note and tagging index caches fed into the PrivateExecutionOracle (along with some other auxiliary
@@ -344,7 +364,7 @@ export class TXESession implements TXESessionStateHandler {
344
364
  // difference resides in that the simulator has all information needed in order to run the simulation, while ours
345
365
  // will be ongoing as the different oracles will be invoked from the Noir test, until eventually the private
346
366
  // execution finishes.
347
- this.state = { name: 'PRIVATE', nextBlockGlobalVariables, protocolNullifier, noteCache, taggingIndexCache };
367
+ this.state = { name: 'PRIVATE', nextBlockGlobalVariables, noteCache, taggingIndexCache };
348
368
  this.logger.debug(`Entered state ${this.state.name}`);
349
369
 
350
370
  return (this.oracleHandler as PrivateExecutionOracle).getPrivateContextInputs();
@@ -377,6 +397,8 @@ export class TXESession implements TXESessionStateHandler {
377
397
  async enterUtilityState(contractAddress: AztecAddress = DEFAULT_ADDRESS) {
378
398
  this.exitTopLevelState();
379
399
 
400
+ const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
401
+
380
402
  // There is no automatic message discovery and contract-driven syncing process in inlined private or utility
381
403
  // contexts, which means that known nullifiers are also not searched for, since it is during the tagging sync that
382
404
  // we perform this. We therefore search for known nullifiers now, as otherwise notes that were nullified would not
@@ -385,11 +407,10 @@ export class TXESession implements TXESessionStateHandler {
385
407
  await new NoteService(
386
408
  this.noteStore,
387
409
  this.stateMachine.node,
388
- this.stateMachine.anchorBlockStore,
410
+ anchorBlockHeader,
411
+ this.currentJobId,
389
412
  ).syncNoteNullifiers(contractAddress);
390
413
 
391
- const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
392
-
393
414
  this.oracleHandler = new UtilityExecutionOracle(
394
415
  contractAddress,
395
416
  [],
@@ -400,11 +421,11 @@ export class TXESession implements TXESessionStateHandler {
400
421
  this.keyStore,
401
422
  this.addressStore,
402
423
  this.stateMachine.node,
403
- this.stateMachine.anchorBlockStore,
404
424
  this.recipientTaggingStore,
405
425
  this.senderAddressBookStore,
406
426
  this.capsuleStore,
407
427
  this.privateEventStore,
428
+ this.currentJobId,
408
429
  );
409
430
 
410
431
  this.state = { name: 'UTILITY' };
@@ -440,11 +461,7 @@ export class TXESession implements TXESessionStateHandler {
440
461
  // We rely on the note cache to determine the effects of the transaction. This is incomplete as it doesn't private
441
462
  // logs (other effects like enqueued public calls don't need to be considered since those are not allowed).
442
463
 
443
- const txEffect = await makeTxEffect(
444
- this.state.noteCache,
445
- this.state.protocolNullifier,
446
- this.state.nextBlockGlobalVariables.blockNumber,
447
- );
464
+ const txEffect = await makeTxEffect(this.state.noteCache, this.state.nextBlockGlobalVariables.blockNumber);
448
465
 
449
466
  // We build a block holding just this transaction
450
467
  const forkedWorldTrees = await this.stateMachine.synchronizer.nativeWorldStateService.fork();
@@ -494,11 +511,11 @@ export class TXESession implements TXESessionStateHandler {
494
511
  this.keyStore,
495
512
  this.addressStore,
496
513
  this.stateMachine.node,
497
- this.stateMachine.anchorBlockStore,
498
514
  this.recipientTaggingStore,
499
515
  this.senderAddressBookStore,
500
516
  this.capsuleStore,
501
517
  this.privateEventStore,
518
+ this.currentJobId,
502
519
  );
503
520
  await new WASMSimulator()
504
521
  .executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback())
@@ -4,13 +4,12 @@ import {
4
4
  NULLIFIER_SUBTREE_HEIGHT,
5
5
  NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
6
6
  } from '@aztec/constants';
7
- import { BlockNumber } from '@aztec/foundation/branded-types';
7
+ import { BlockNumber, CheckpointNumber, IndexWithinCheckpoint } from '@aztec/foundation/branded-types';
8
8
  import { padArrayEnd } from '@aztec/foundation/collection';
9
9
  import { Fr } from '@aztec/foundation/curves/bn254';
10
- import { Body, L2Block, L2BlockHeader } from '@aztec/stdlib/block';
11
- import { makeContentCommitment } from '@aztec/stdlib/testing';
10
+ import { Body, L2Block } from '@aztec/stdlib/block';
12
11
  import { AppendOnlyTreeSnapshot, MerkleTreeId, type MerkleTreeWriteOperations } from '@aztec/stdlib/trees';
13
- import { GlobalVariables, TxEffect } from '@aztec/stdlib/tx';
12
+ import { BlockHeader, GlobalVariables, TxEffect } from '@aztec/stdlib/tx';
14
13
 
15
14
  /**
16
15
  * Returns a transaction request hash that is valid for transactions that are the only ones in a block.
@@ -47,20 +46,18 @@ export async function insertTxEffectIntoWorldTrees(
47
46
  export async function makeTXEBlockHeader(
48
47
  worldTrees: MerkleTreeWriteOperations,
49
48
  globalVariables: GlobalVariables,
50
- ): Promise<L2BlockHeader> {
49
+ ): Promise<BlockHeader> {
51
50
  const stateReference = await worldTrees.getStateReference();
52
51
  const archiveInfo = await worldTrees.getTreeInfo(MerkleTreeId.ARCHIVE);
53
52
 
54
- return new L2BlockHeader(
55
- new AppendOnlyTreeSnapshot(new Fr(archiveInfo.root), Number(archiveInfo.size)),
56
- makeContentCommitment(),
57
- stateReference,
53
+ return BlockHeader.from({
54
+ lastArchive: new AppendOnlyTreeSnapshot(new Fr(archiveInfo.root), Number(archiveInfo.size)),
55
+ spongeBlobHash: Fr.ZERO,
56
+ state: stateReference,
58
57
  globalVariables,
59
- Fr.ZERO,
60
- Fr.ZERO,
61
- Fr.ZERO,
62
- Fr.ZERO,
63
- );
58
+ totalFees: Fr.ZERO,
59
+ totalManaUsed: Fr.ZERO,
60
+ });
64
61
  }
65
62
 
66
63
  /**
@@ -84,11 +81,15 @@ export async function makeTXEBlock(
84
81
  const header = await makeTXEBlockHeader(worldTrees, globalVariables);
85
82
 
86
83
  // Update the archive tree with this block's header hash
87
- await worldTrees.updateArchive(header.toBlockHeader());
84
+ await worldTrees.updateArchive(header);
88
85
 
89
86
  // Get the new archive state after updating
90
87
  const newArchiveInfo = await worldTrees.getTreeInfo(MerkleTreeId.ARCHIVE);
91
88
  const newArchive = new AppendOnlyTreeSnapshot(new Fr(newArchiveInfo.root), Number(newArchiveInfo.size));
92
89
 
93
- return new L2Block(newArchive, header, new Body(txEffects));
90
+ // L2Block requires checkpointNumber and indexWithinCheckpoint
91
+ const checkpointNumber = CheckpointNumber.fromBlockNumber(globalVariables.blockNumber);
92
+ const indexWithinCheckpoint = IndexWithinCheckpoint(0);
93
+
94
+ return new L2Block(newArchive, header, new Body(txEffects), checkpointNumber, indexWithinCheckpoint);
94
95
  }
@@ -4,16 +4,12 @@ import type { ExecutionNoteCache } from '@aztec/pxe/simulator';
4
4
  import { computeNoteHashNonce, computeUniqueNoteHash, siloNoteHash } from '@aztec/stdlib/hash';
5
5
  import { TxEffect, TxHash } from '@aztec/stdlib/tx';
6
6
 
7
- export async function makeTxEffect(
8
- noteCache: ExecutionNoteCache,
9
- protocolNullifier: Fr,
10
- txBlockNumber: BlockNumber,
11
- ): Promise<TxEffect> {
7
+ export async function makeTxEffect(noteCache: ExecutionNoteCache, txBlockNumber: BlockNumber): Promise<TxEffect> {
12
8
  const txEffect = TxEffect.empty();
13
9
 
14
- const { usedProtocolNullifierForNonces } = noteCache.finish();
15
- const nonceGenerator = usedProtocolNullifierForNonces ? protocolNullifier : noteCache.getAllNullifiers()[0];
10
+ noteCache.finish();
16
11
 
12
+ const nonceGenerator = noteCache.getNonceGenerator();
17
13
  txEffect.noteHashes = await Promise.all(
18
14
  noteCache
19
15
  .getAllNotes()
@@ -28,10 +24,6 @@ export async function makeTxEffect(
28
24
  // Nullifiers are already siloed
29
25
  txEffect.nullifiers = noteCache.getAllNullifiers();
30
26
 
31
- if (usedProtocolNullifierForNonces) {
32
- txEffect.nullifiers.unshift(protocolNullifier);
33
- }
34
-
35
27
  txEffect.txHash = new TxHash(new Fr(txBlockNumber));
36
28
 
37
29
  return txEffect;