@aztec/sequencer-client 0.1.0-alpha11

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 (146) hide show
  1. package/.eslintrc.cjs +1 -0
  2. package/.tsbuildinfo +1 -0
  3. package/README.md +45 -0
  4. package/dest/block_builder/index.d.ts +19 -0
  5. package/dest/block_builder/index.d.ts.map +1 -0
  6. package/dest/block_builder/index.js +2 -0
  7. package/dest/block_builder/solo_block_builder.d.ts +76 -0
  8. package/dest/block_builder/solo_block_builder.d.ts.map +1 -0
  9. package/dest/block_builder/solo_block_builder.js +453 -0
  10. package/dest/block_builder/solo_block_builder.test.d.ts +3 -0
  11. package/dest/block_builder/solo_block_builder.test.d.ts.map +1 -0
  12. package/dest/block_builder/solo_block_builder.test.js +291 -0
  13. package/dest/block_builder/types.d.ts +12 -0
  14. package/dest/block_builder/types.d.ts.map +1 -0
  15. package/dest/block_builder/types.js +2 -0
  16. package/dest/client/index.d.ts +2 -0
  17. package/dest/client/index.d.ts.map +1 -0
  18. package/dest/client/index.js +2 -0
  19. package/dest/client/sequencer-client.d.ts +32 -0
  20. package/dest/client/sequencer-client.d.ts.map +1 -0
  21. package/dest/client/sequencer-client.js +47 -0
  22. package/dest/config.d.ts +12 -0
  23. package/dest/config.d.ts.map +1 -0
  24. package/dest/config.js +25 -0
  25. package/dest/global_variable_builder/config.d.ts +19 -0
  26. package/dest/global_variable_builder/config.d.ts.map +1 -0
  27. package/dest/global_variable_builder/config.js +2 -0
  28. package/dest/global_variable_builder/global_builder.d.ts +30 -0
  29. package/dest/global_variable_builder/global_builder.d.ts.map +1 -0
  30. package/dest/global_variable_builder/global_builder.js +24 -0
  31. package/dest/global_variable_builder/index.d.ts +11 -0
  32. package/dest/global_variable_builder/index.d.ts.map +1 -0
  33. package/dest/global_variable_builder/index.js +12 -0
  34. package/dest/global_variable_builder/viem-reader.d.ts +26 -0
  35. package/dest/global_variable_builder/viem-reader.d.ts.map +1 -0
  36. package/dest/global_variable_builder/viem-reader.js +43 -0
  37. package/dest/index.d.ts +11 -0
  38. package/dest/index.d.ts.map +1 -0
  39. package/dest/index.js +12 -0
  40. package/dest/mocks/tx.d.ts +10 -0
  41. package/dest/mocks/tx.d.ts.map +1 -0
  42. package/dest/mocks/tx.js +18 -0
  43. package/dest/mocks/verification_keys.d.ts +28 -0
  44. package/dest/mocks/verification_keys.d.ts.map +1 -0
  45. package/dest/mocks/verification_keys.js +14 -0
  46. package/dest/prover/empty.d.ts +41 -0
  47. package/dest/prover/empty.d.ts.map +1 -0
  48. package/dest/prover/empty.js +57 -0
  49. package/dest/prover/index.d.ts +17 -0
  50. package/dest/prover/index.d.ts.map +1 -0
  51. package/dest/prover/index.js +2 -0
  52. package/dest/publisher/config.d.ts +33 -0
  53. package/dest/publisher/config.d.ts.map +1 -0
  54. package/dest/publisher/config.js +2 -0
  55. package/dest/publisher/index.d.ts +10 -0
  56. package/dest/publisher/index.d.ts.map +1 -0
  57. package/dest/publisher/index.js +11 -0
  58. package/dest/publisher/l1-publisher.d.ts +99 -0
  59. package/dest/publisher/l1-publisher.d.ts.map +1 -0
  60. package/dest/publisher/l1-publisher.js +154 -0
  61. package/dest/publisher/l1-publisher.test.d.ts +2 -0
  62. package/dest/publisher/l1-publisher.test.d.ts.map +1 -0
  63. package/dest/publisher/l1-publisher.test.js +58 -0
  64. package/dest/publisher/viem-tx-sender.d.ts +42 -0
  65. package/dest/publisher/viem-tx-sender.d.ts.map +1 -0
  66. package/dest/publisher/viem-tx-sender.js +115 -0
  67. package/dest/receiver.d.ts +13 -0
  68. package/dest/receiver.d.ts.map +1 -0
  69. package/dest/receiver.js +2 -0
  70. package/dest/sequencer/config.d.ts +26 -0
  71. package/dest/sequencer/config.d.ts.map +1 -0
  72. package/dest/sequencer/config.js +2 -0
  73. package/dest/sequencer/index.d.ts +4 -0
  74. package/dest/sequencer/index.d.ts.map +1 -0
  75. package/dest/sequencer/index.js +4 -0
  76. package/dest/sequencer/processed_tx.d.ts +34 -0
  77. package/dest/sequencer/processed_tx.d.ts.map +1 -0
  78. package/dest/sequencer/processed_tx.js +39 -0
  79. package/dest/sequencer/public_processor.d.ts +68 -0
  80. package/dest/sequencer/public_processor.d.ts.map +1 -0
  81. package/dest/sequencer/public_processor.js +195 -0
  82. package/dest/sequencer/public_processor.test.d.ts +2 -0
  83. package/dest/sequencer/public_processor.test.d.ts.map +1 -0
  84. package/dest/sequencer/public_processor.test.js +159 -0
  85. package/dest/sequencer/sequencer.d.ts +133 -0
  86. package/dest/sequencer/sequencer.d.ts.map +1 -0
  87. package/dest/sequencer/sequencer.js +293 -0
  88. package/dest/sequencer/sequencer.test.d.ts +2 -0
  89. package/dest/sequencer/sequencer.test.d.ts.map +1 -0
  90. package/dest/sequencer/sequencer.test.js +100 -0
  91. package/dest/sequencer/utils.d.ts +7 -0
  92. package/dest/sequencer/utils.d.ts.map +1 -0
  93. package/dest/sequencer/utils.js +9 -0
  94. package/dest/simulator/index.d.ts +42 -0
  95. package/dest/simulator/index.d.ts.map +1 -0
  96. package/dest/simulator/index.js +2 -0
  97. package/dest/simulator/public_executor.d.ts +39 -0
  98. package/dest/simulator/public_executor.d.ts.map +1 -0
  99. package/dest/simulator/public_executor.js +118 -0
  100. package/dest/simulator/public_kernel.d.ts +20 -0
  101. package/dest/simulator/public_kernel.d.ts.map +1 -0
  102. package/dest/simulator/public_kernel.js +27 -0
  103. package/dest/simulator/rollup.d.ts +33 -0
  104. package/dest/simulator/rollup.d.ts.map +1 -0
  105. package/dest/simulator/rollup.js +41 -0
  106. package/dest/utils.d.ts +12 -0
  107. package/dest/utils.d.ts.map +1 -0
  108. package/dest/utils.js +16 -0
  109. package/jest.integration.config.json +13 -0
  110. package/package.json +23 -0
  111. package/src/block_builder/index.ts +23 -0
  112. package/src/block_builder/solo_block_builder.test.ts +425 -0
  113. package/src/block_builder/solo_block_builder.ts +710 -0
  114. package/src/block_builder/types.ts +16 -0
  115. package/src/client/index.ts +1 -0
  116. package/src/client/sequencer-client.ts +79 -0
  117. package/src/config.ts +48 -0
  118. package/src/global_variable_builder/config.ts +19 -0
  119. package/src/global_variable_builder/global_builder.ts +43 -0
  120. package/src/global_variable_builder/index.ts +15 -0
  121. package/src/global_variable_builder/viem-reader.ts +63 -0
  122. package/src/index.ts +12 -0
  123. package/src/mocks/tx.ts +26 -0
  124. package/src/mocks/verification_keys.ts +36 -0
  125. package/src/prover/empty.ts +73 -0
  126. package/src/prover/index.ts +27 -0
  127. package/src/publisher/config.ts +36 -0
  128. package/src/publisher/index.ts +14 -0
  129. package/src/publisher/l1-publisher.test.ts +77 -0
  130. package/src/publisher/l1-publisher.ts +236 -0
  131. package/src/publisher/viem-tx-sender.ts +166 -0
  132. package/src/receiver.ts +13 -0
  133. package/src/sequencer/config.ts +27 -0
  134. package/src/sequencer/index.ts +3 -0
  135. package/src/sequencer/processed_tx.ts +82 -0
  136. package/src/sequencer/public_processor.test.ts +225 -0
  137. package/src/sequencer/public_processor.ts +280 -0
  138. package/src/sequencer/sequencer.test.ts +158 -0
  139. package/src/sequencer/sequencer.ts +356 -0
  140. package/src/sequencer/utils.ts +18 -0
  141. package/src/simulator/index.ts +51 -0
  142. package/src/simulator/public_executor.ts +137 -0
  143. package/src/simulator/public_kernel.ts +27 -0
  144. package/src/simulator/rollup.ts +54 -0
  145. package/src/utils.ts +16 -0
  146. package/tsconfig.json +38 -0
@@ -0,0 +1,225 @@
1
+ import { PublicExecution, PublicExecutionResult, PublicExecutor } from '@aztec/acir-simulator';
2
+ import {
3
+ ARGS_LENGTH,
4
+ CallContext,
5
+ CircuitsWasm,
6
+ EthAddress,
7
+ Fr,
8
+ FunctionData,
9
+ GlobalVariables,
10
+ KERNEL_PRIVATE_CALL_STACK_LENGTH,
11
+ KERNEL_PUBLIC_CALL_STACK_LENGTH,
12
+ PUBLIC_DATA_TREE_HEIGHT,
13
+ Proof,
14
+ PublicCallRequest,
15
+ makeEmptyProof,
16
+ makeTuple,
17
+ } from '@aztec/circuits.js';
18
+ import { computeCallStackItemHash } from '@aztec/circuits.js/abis';
19
+ import {
20
+ makeAztecAddress,
21
+ makeKernelPublicInputs,
22
+ makePublicCallRequest,
23
+ makeSelector,
24
+ } from '@aztec/circuits.js/factories';
25
+ import { padArrayEnd } from '@aztec/foundation/collection';
26
+ import { SiblingPath } from '@aztec/merkle-tree';
27
+ import {
28
+ ContractDataSource,
29
+ ContractPublicData,
30
+ EncodedContractFunction,
31
+ ExecutionRequest,
32
+ FunctionL2Logs,
33
+ Tx,
34
+ TxL2Logs,
35
+ } from '@aztec/types';
36
+ import { MerkleTreeOperations, TreeInfo } from '@aztec/world-state';
37
+ import { MockProxy, mock } from 'jest-mock-extended';
38
+ import pick from 'lodash.pick';
39
+ import times from 'lodash.times';
40
+ import { makeTx } from '../index.js';
41
+ import { PublicProver } from '../prover/index.js';
42
+ import { PublicKernelCircuitSimulator } from '../simulator/index.js';
43
+ import { WasmPublicKernelCircuitSimulator } from '../simulator/public_kernel.js';
44
+ import { PublicProcessor } from './public_processor.js';
45
+
46
+ describe('public_processor', () => {
47
+ let db: MockProxy<MerkleTreeOperations>;
48
+ let publicExecutor: MockProxy<PublicExecutor>;
49
+ let publicProver: MockProxy<PublicProver>;
50
+ let contractDataSource: MockProxy<ContractDataSource>;
51
+
52
+ let publicFunction: EncodedContractFunction;
53
+ let contractData: ContractPublicData;
54
+ let proof: Proof;
55
+ let root: Buffer;
56
+
57
+ let processor: PublicProcessor;
58
+
59
+ beforeEach(() => {
60
+ db = mock<MerkleTreeOperations>();
61
+ publicExecutor = mock<PublicExecutor>();
62
+ publicProver = mock<PublicProver>();
63
+ contractDataSource = mock<ContractDataSource>();
64
+
65
+ contractData = ContractPublicData.random();
66
+ publicFunction = EncodedContractFunction.random();
67
+ proof = makeEmptyProof();
68
+ root = Buffer.alloc(32, 5);
69
+
70
+ publicProver.getPublicCircuitProof.mockResolvedValue(proof);
71
+ publicProver.getPublicKernelCircuitProof.mockResolvedValue(proof);
72
+ db.getTreeInfo.mockResolvedValue({ root } as TreeInfo);
73
+ contractDataSource.getL2ContractPublicData.mockResolvedValue(contractData);
74
+ contractDataSource.getPublicFunction.mockResolvedValue(publicFunction);
75
+ });
76
+
77
+ describe('with mock circuits', () => {
78
+ let publicKernel: MockProxy<PublicKernelCircuitSimulator>;
79
+
80
+ beforeEach(() => {
81
+ publicKernel = mock<PublicKernelCircuitSimulator>();
82
+ processor = new PublicProcessor(db, publicExecutor, publicKernel, publicProver, contractDataSource);
83
+ });
84
+
85
+ it('skips txs without public execution requests', async function () {
86
+ const tx = makeTx();
87
+ tx.data.end.publicCallStack = makeTuple(KERNEL_PUBLIC_CALL_STACK_LENGTH, Fr.zero);
88
+ const hash = await tx.getTxHash();
89
+ const [processed, failed] = await processor.process([tx], GlobalVariables.empty());
90
+
91
+ expect(processed).toEqual([
92
+ { isEmpty: false, hash, ...pick(tx, 'data', 'proof', 'encryptedLogs', 'unencryptedLogs') },
93
+ ]);
94
+ expect(failed).toEqual([]);
95
+ });
96
+
97
+ it('returns failed txs without aborting entire operation', async function () {
98
+ publicExecutor.execute.mockRejectedValue(new Error(`Failed`));
99
+
100
+ const tx = makeTx();
101
+ const [processed, failed] = await processor.process([tx], GlobalVariables.empty());
102
+
103
+ expect(processed).toEqual([]);
104
+ expect(failed).toEqual([tx]);
105
+ });
106
+ });
107
+
108
+ describe('with actual circuits', () => {
109
+ let publicKernel: PublicKernelCircuitSimulator;
110
+ let wasm: CircuitsWasm;
111
+
112
+ beforeAll(async () => {
113
+ wasm = await CircuitsWasm.get();
114
+ });
115
+
116
+ beforeEach(() => {
117
+ const path = times(PUBLIC_DATA_TREE_HEIGHT, i => Buffer.alloc(32, i));
118
+ db.getSiblingPath.mockResolvedValue(new SiblingPath<number>(PUBLIC_DATA_TREE_HEIGHT, path));
119
+ publicKernel = new WasmPublicKernelCircuitSimulator();
120
+ processor = new PublicProcessor(db, publicExecutor, publicKernel, publicProver, contractDataSource);
121
+ });
122
+
123
+ const expectedTxByHash = async (tx: Tx) =>
124
+ expect.objectContaining({
125
+ hash: await tx.getTxHash(),
126
+ proof,
127
+ });
128
+
129
+ it('runs a tx with enqueued public calls', async function () {
130
+ const callRequests: PublicCallRequest[] = [makePublicCallRequest(0x100), makePublicCallRequest(0x100)];
131
+ const callStackItems = await Promise.all(callRequests.map(call => call.toPublicCallStackItem()));
132
+ const callStackHashes = callStackItems.map(call => computeCallStackItemHash(wasm, call));
133
+
134
+ const kernelOutput = makeKernelPublicInputs(0x10);
135
+ kernelOutput.end.publicCallStack = padArrayEnd(callStackHashes, Fr.ZERO, KERNEL_PUBLIC_CALL_STACK_LENGTH);
136
+ kernelOutput.end.privateCallStack = padArrayEnd([], Fr.ZERO, KERNEL_PRIVATE_CALL_STACK_LENGTH);
137
+
138
+ const tx = new Tx(kernelOutput, proof, TxL2Logs.random(2, 3), TxL2Logs.random(3, 2), [], callRequests);
139
+
140
+ publicExecutor.execute.mockImplementation(execution => {
141
+ for (const request of callRequests) {
142
+ if (execution.contractAddress.equals(request.contractAddress)) {
143
+ return Promise.resolve(makePublicExecutionResultFromRequest(request));
144
+ }
145
+ }
146
+ throw new Error(`Unexpected execution request: ${execution}`);
147
+ });
148
+
149
+ const [processed, failed] = await processor.process([tx], GlobalVariables.empty());
150
+
151
+ expect(processed).toHaveLength(1);
152
+ expect(processed).toEqual([await expectedTxByHash(tx)]);
153
+ expect(failed).toHaveLength(0);
154
+ expect(publicExecutor.execute).toHaveBeenCalledTimes(2);
155
+ });
156
+
157
+ it('runs a tx with an enqueued public call with nested execution', async function () {
158
+ const callRequest: PublicCallRequest = makePublicCallRequest(0x100);
159
+ const callStackItem = await callRequest.toPublicCallStackItem();
160
+ const callStackHash = computeCallStackItemHash(wasm, callStackItem);
161
+
162
+ const kernelOutput = makeKernelPublicInputs(0x10);
163
+ kernelOutput.end.publicCallStack = padArrayEnd([callStackHash], Fr.ZERO, KERNEL_PUBLIC_CALL_STACK_LENGTH);
164
+ kernelOutput.end.privateCallStack = padArrayEnd([], Fr.ZERO, KERNEL_PRIVATE_CALL_STACK_LENGTH);
165
+
166
+ const tx = new Tx(kernelOutput, proof, TxL2Logs.random(2, 3), TxL2Logs.random(3, 2), [], [callRequest]);
167
+
168
+ const publicExecutionResult = makePublicExecutionResultFromRequest(callRequest);
169
+ publicExecutionResult.nestedExecutions = [
170
+ makePublicExecutionResult({
171
+ from: publicExecutionResult.execution.contractAddress,
172
+ to: makeAztecAddress(30),
173
+ functionData: new FunctionData(makeSelector(5), false, false),
174
+ args: new Array(ARGS_LENGTH).fill(Fr.ZERO),
175
+ }),
176
+ ];
177
+ publicExecutor.execute.mockResolvedValue(publicExecutionResult);
178
+
179
+ const [processed, failed] = await processor.process([tx], GlobalVariables.empty());
180
+
181
+ expect(processed).toHaveLength(1);
182
+ expect(processed).toEqual([await expectedTxByHash(tx)]);
183
+ expect(failed).toHaveLength(0);
184
+ expect(publicExecutor.execute).toHaveBeenCalledTimes(1);
185
+ });
186
+ });
187
+ });
188
+
189
+ function makePublicExecutionResultFromRequest(item: PublicCallRequest): PublicExecutionResult {
190
+ return {
191
+ execution: item,
192
+ nestedExecutions: [],
193
+ returnValues: [new Fr(1n)],
194
+ newCommitments: [],
195
+ newL2ToL1Messages: [],
196
+ newNullifiers: [],
197
+ contractStorageReads: [],
198
+ contractStorageUpdateRequests: [],
199
+ unencryptedLogs: new FunctionL2Logs([]),
200
+ };
201
+ }
202
+
203
+ function makePublicExecutionResult(
204
+ tx: ExecutionRequest,
205
+ nestedExecutions: PublicExecutionResult[] = [],
206
+ ): PublicExecutionResult {
207
+ const callContext = new CallContext(tx.from, tx.to, EthAddress.ZERO, false, false, false);
208
+ const execution: PublicExecution = {
209
+ callContext,
210
+ contractAddress: tx.to,
211
+ functionData: tx.functionData,
212
+ args: tx.args,
213
+ };
214
+ return {
215
+ execution,
216
+ nestedExecutions,
217
+ returnValues: [],
218
+ newCommitments: [],
219
+ newNullifiers: [],
220
+ newL2ToL1Messages: [],
221
+ contractStorageReads: [],
222
+ contractStorageUpdateRequests: [],
223
+ unencryptedLogs: new FunctionL2Logs([]),
224
+ };
225
+ }
@@ -0,0 +1,280 @@
1
+ import { PublicExecution, PublicExecutionResult, PublicExecutor, isPublicExecutionResult } from '@aztec/acir-simulator';
2
+ import {
3
+ AztecAddress,
4
+ CircuitsWasm,
5
+ ContractStorageRead,
6
+ ContractStorageUpdateRequest,
7
+ Fr,
8
+ GlobalVariables,
9
+ KERNEL_PUBLIC_DATA_READS_LENGTH,
10
+ KERNEL_PUBLIC_DATA_UPDATE_REQUESTS_LENGTH,
11
+ KernelCircuitPublicInputs,
12
+ MembershipWitness,
13
+ NEW_COMMITMENTS_LENGTH,
14
+ NEW_L2_TO_L1_MSGS_LENGTH,
15
+ NEW_NULLIFIERS_LENGTH,
16
+ PUBLIC_CALL_STACK_LENGTH,
17
+ PreviousKernelData,
18
+ Proof,
19
+ PublicCallData,
20
+ PublicCallStackItem,
21
+ PublicCircuitPublicInputs,
22
+ PublicKernelInputs,
23
+ PublicKernelPublicInputs,
24
+ RETURN_VALUES_LENGTH,
25
+ VK_TREE_HEIGHT,
26
+ } from '@aztec/circuits.js';
27
+ import { computeCallStackItemHash, computeVarArgsHash } from '@aztec/circuits.js/abis';
28
+ import { isArrayEmpty, padArrayEnd, padArrayStart } from '@aztec/foundation/collection';
29
+ import { createDebugLogger } from '@aztec/foundation/log';
30
+ import { Tuple, mapTuple, to2Fields } from '@aztec/foundation/serialize';
31
+ import { ContractDataSource, FunctionL2Logs, L1ToL2MessageSource, MerkleTreeId, Tx } from '@aztec/types';
32
+ import { MerkleTreeOperations } from '@aztec/world-state';
33
+ import { getVerificationKeys } from '../index.js';
34
+ import { EmptyPublicProver } from '../prover/empty.js';
35
+ import { PublicProver } from '../prover/index.js';
36
+ import { PublicKernelCircuitSimulator } from '../simulator/index.js';
37
+ import { getPublicExecutor } from '../simulator/public_executor.js';
38
+ import { WasmPublicKernelCircuitSimulator } from '../simulator/public_kernel.js';
39
+ import { ProcessedTx, makeEmptyProcessedTx, makeProcessedTx } from './processed_tx.js';
40
+ import { getCombinedHistoricTreeRoots } from './utils.js';
41
+
42
+ /**
43
+ * Creates new instances of PublicProcessor given the provided merkle tree db and contract data source.
44
+ */
45
+ export class PublicProcessorFactory {
46
+ constructor(
47
+ private merkleTree: MerkleTreeOperations,
48
+ private contractDataSource: ContractDataSource,
49
+ private l1Tol2MessagesDataSource: L1ToL2MessageSource,
50
+ ) {}
51
+
52
+ /**
53
+ * Creates a new instance of a PublicProcessor.
54
+ * @returns A new instance of a PublicProcessor.
55
+ */
56
+ public create() {
57
+ return new PublicProcessor(
58
+ this.merkleTree,
59
+ getPublicExecutor(this.merkleTree, this.contractDataSource, this.l1Tol2MessagesDataSource),
60
+ new WasmPublicKernelCircuitSimulator(),
61
+ new EmptyPublicProver(),
62
+ this.contractDataSource,
63
+ );
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Converts Txs lifted from the P2P module into ProcessedTx objects by executing
69
+ * any public function calls in them. Txs with private calls only are unaffected.
70
+ */
71
+ export class PublicProcessor {
72
+ constructor(
73
+ protected db: MerkleTreeOperations,
74
+ protected publicExecutor: PublicExecutor,
75
+ protected publicKernel: PublicKernelCircuitSimulator,
76
+ protected publicProver: PublicProver,
77
+ protected contractDataSource: ContractDataSource,
78
+
79
+ private log = createDebugLogger('aztec:sequencer:public-processor'),
80
+ ) {}
81
+
82
+ /**
83
+ * Run each tx through the public circuit and the public kernel circuit if needed.
84
+ * @param txs - Txs to process.
85
+ * @param globalVariables - The global variables for the block.
86
+ * @returns The list of processed txs with their circuit simulation outputs.
87
+ */
88
+ public async process(txs: Tx[], globalVariables: GlobalVariables): Promise<[ProcessedTx[], Tx[]]> {
89
+ const result: ProcessedTx[] = [];
90
+ const failed: Tx[] = [];
91
+
92
+ for (const tx of txs) {
93
+ this.log(`Processing tx ${await tx.getTxHash()}`);
94
+ try {
95
+ result.push(await this.processTx(tx, globalVariables));
96
+ } catch (err) {
97
+ this.log(`Error processing tx ${await tx.getTxHash()}: ${err}`);
98
+ failed.push(tx);
99
+ }
100
+ }
101
+ return [result, failed];
102
+ }
103
+
104
+ /**
105
+ * Makes an empty processed tx. Useful for padding a block to a power of two number of txs.
106
+ * @param chainId - The chain id of the rollup.
107
+ * @param version - The version of the rollup.
108
+ * @returns A processed tx with empty data.
109
+ */
110
+ public async makeEmptyProcessedTx(chainId: Fr, version: Fr): Promise<ProcessedTx> {
111
+ const historicTreeRoots = await getCombinedHistoricTreeRoots(this.db);
112
+ return makeEmptyProcessedTx(historicTreeRoots, chainId, version);
113
+ }
114
+
115
+ protected async processTx(tx: Tx, globalVariables: GlobalVariables): Promise<ProcessedTx> {
116
+ if (!isArrayEmpty(tx.data.end.publicCallStack, item => item.isZero())) {
117
+ const [publicKernelOutput, publicKernelProof, newUnencryptedFunctionLogs] = await this.processEnqueuedPublicCalls(
118
+ tx,
119
+ globalVariables,
120
+ );
121
+ tx.unencryptedLogs.addFunctionLogs(newUnencryptedFunctionLogs);
122
+
123
+ return makeProcessedTx(tx, publicKernelOutput, publicKernelProof);
124
+ } else {
125
+ return makeProcessedTx(tx);
126
+ }
127
+ }
128
+
129
+ protected async processEnqueuedPublicCalls(
130
+ tx: Tx,
131
+ globalVariables: GlobalVariables,
132
+ ): Promise<[PublicKernelPublicInputs, Proof, FunctionL2Logs[]]> {
133
+ this.log(`Executing enqueued public calls for tx ${await tx.getTxHash()}`);
134
+ if (!tx.enqueuedPublicFunctionCalls) throw new Error(`Missing preimages for enqueued public calls`);
135
+
136
+ // We execute the requests in order, which means reversing the input as the stack pops from the end of the array
137
+ const executionStack: (PublicExecution | PublicExecutionResult)[] = [...tx.enqueuedPublicFunctionCalls].reverse();
138
+
139
+ let kernelOutput = tx.data;
140
+ let kernelProof = tx.proof;
141
+ const newUnencryptedFunctionLogs: FunctionL2Logs[] = [];
142
+
143
+ while (executionStack.length) {
144
+ const current = executionStack.pop()!;
145
+ const isExecutionRequest = !isPublicExecutionResult(current);
146
+ const result = isExecutionRequest ? await this.publicExecutor.execute(current, globalVariables) : current;
147
+ newUnencryptedFunctionLogs.push(result.unencryptedLogs);
148
+ const functionSelector = result.execution.functionData.functionSelectorBuffer.toString('hex');
149
+ this.log(`Running public kernel circuit for ${functionSelector}@${result.execution.contractAddress.toString()}`);
150
+ executionStack.push(...result.nestedExecutions);
151
+ const preimages = await this.getPublicCallStackPreimages(result);
152
+ const callData = await this.getPublicCallData(result, preimages, isExecutionRequest);
153
+ [kernelOutput, kernelProof] = await this.runKernelCircuit(callData, kernelOutput, kernelProof);
154
+ }
155
+
156
+ return [kernelOutput, kernelProof, newUnencryptedFunctionLogs];
157
+ }
158
+
159
+ protected async runKernelCircuit(
160
+ callData: PublicCallData,
161
+ previousOutput: KernelCircuitPublicInputs,
162
+ previousProof: Proof,
163
+ ): Promise<[KernelCircuitPublicInputs, Proof]> {
164
+ const output = await this.getKernelCircuitOutput(callData, previousOutput, previousProof);
165
+ const proof = await this.publicProver.getPublicKernelCircuitProof(output);
166
+ return [output, proof];
167
+ }
168
+
169
+ protected getKernelCircuitOutput(
170
+ callData: PublicCallData,
171
+ previousOutput: KernelCircuitPublicInputs,
172
+ previousProof: Proof,
173
+ ): Promise<KernelCircuitPublicInputs> {
174
+ if (previousOutput?.isPrivate && previousProof) {
175
+ // Run the public kernel circuit with previous private kernel
176
+ const previousKernel = this.getPreviousKernelData(previousOutput, previousProof);
177
+ const inputs = new PublicKernelInputs(previousKernel, callData);
178
+ return this.publicKernel.publicKernelCircuitPrivateInput(inputs);
179
+ } else if (previousOutput && previousProof) {
180
+ // Run the public kernel circuit with previous public kernel
181
+ const previousKernel = this.getPreviousKernelData(previousOutput, previousProof);
182
+ const inputs = new PublicKernelInputs(previousKernel, callData);
183
+ return this.publicKernel.publicKernelCircuitNonFirstIteration(inputs);
184
+ } else {
185
+ throw new Error(`No public kernel circuit for inputs`);
186
+ }
187
+ }
188
+
189
+ protected getPreviousKernelData(previousOutput: KernelCircuitPublicInputs, previousProof: Proof): PreviousKernelData {
190
+ const vk = getVerificationKeys().publicKernelCircuit;
191
+ const vkIndex = 0;
192
+ const vkSiblingPath = MembershipWitness.random(VK_TREE_HEIGHT).siblingPath;
193
+ return new PreviousKernelData(previousOutput, previousProof, vk, vkIndex, vkSiblingPath);
194
+ }
195
+
196
+ protected async getPublicCircuitPublicInputs(result: PublicExecutionResult) {
197
+ const publicDataTreeInfo = await this.db.getTreeInfo(MerkleTreeId.PUBLIC_DATA_TREE);
198
+ const historicPublicDataTreeRoot = Fr.fromBuffer(publicDataTreeInfo.root);
199
+ const callStackPreimages = await this.getPublicCallStackPreimages(result);
200
+ const wasm = await CircuitsWasm.get();
201
+ const publicCallStack = mapTuple(callStackPreimages, item =>
202
+ item.isEmpty() ? Fr.zero() : computeCallStackItemHash(wasm, item),
203
+ );
204
+
205
+ // TODO(#1347): Noir fails with too many unknowns error when public inputs struct contains too many members.
206
+ const unencryptedLogsHash = to2Fields(result.unencryptedLogs.hash());
207
+ const unencryptedLogPreimagesLength = new Fr(result.unencryptedLogs.getSerializedLength());
208
+
209
+ return PublicCircuitPublicInputs.from({
210
+ callContext: result.execution.callContext,
211
+ proverAddress: AztecAddress.random(),
212
+ argsHash: await computeVarArgsHash(wasm, result.execution.args),
213
+ newCommitments: padArrayEnd(result.newCommitments, Fr.ZERO, NEW_COMMITMENTS_LENGTH),
214
+ newNullifiers: padArrayEnd(result.newNullifiers, Fr.ZERO, NEW_NULLIFIERS_LENGTH),
215
+ newL2ToL1Msgs: padArrayEnd(result.newL2ToL1Messages, Fr.ZERO, NEW_L2_TO_L1_MSGS_LENGTH),
216
+ returnValues: padArrayEnd(result.returnValues, Fr.ZERO, RETURN_VALUES_LENGTH),
217
+ contractStorageReads: padArrayEnd(
218
+ result.contractStorageReads,
219
+ ContractStorageRead.empty(),
220
+ KERNEL_PUBLIC_DATA_READS_LENGTH,
221
+ ),
222
+ contractStorageUpdateRequests: padArrayEnd(
223
+ result.contractStorageUpdateRequests,
224
+ ContractStorageUpdateRequest.empty(),
225
+ KERNEL_PUBLIC_DATA_UPDATE_REQUESTS_LENGTH,
226
+ ),
227
+ publicCallStack,
228
+ unencryptedLogsHash,
229
+ unencryptedLogPreimagesLength,
230
+ historicPublicDataTreeRoot,
231
+ });
232
+ }
233
+
234
+ protected async getPublicCallStackItem(result: PublicExecutionResult, isExecutionRequest = false) {
235
+ return new PublicCallStackItem(
236
+ result.execution.contractAddress,
237
+ result.execution.functionData,
238
+ await this.getPublicCircuitPublicInputs(result),
239
+ isExecutionRequest,
240
+ );
241
+ }
242
+
243
+ protected async getPublicCallStackPreimages(result: PublicExecutionResult) {
244
+ const nested = result.nestedExecutions;
245
+ const preimages: PublicCallStackItem[] = await Promise.all(nested.map(n => this.getPublicCallStackItem(n)));
246
+ if (preimages.length > PUBLIC_CALL_STACK_LENGTH) {
247
+ throw new Error(`Public call stack size exceeded (max ${PUBLIC_CALL_STACK_LENGTH}, got ${preimages.length})`);
248
+ }
249
+
250
+ // Top of the stack is at the end of the array, so we padStart
251
+ return padArrayStart(preimages, PublicCallStackItem.empty(), PUBLIC_CALL_STACK_LENGTH);
252
+ }
253
+
254
+ protected getBytecodeHash(_result: PublicExecutionResult) {
255
+ // TODO: Determine how to calculate bytecode hash. Circuits just check it isn't zero for now.
256
+ // See https://github.com/AztecProtocol/aztec3-packages/issues/378
257
+ const bytecodeHash = new Fr(1n);
258
+ return Promise.resolve(bytecodeHash);
259
+ }
260
+
261
+ /**
262
+ * Calculates the PublicCircuitOutput for this execution result along with its proof,
263
+ * and assembles a PublicCallData object from it.
264
+ * @param result - The execution result.
265
+ * @param preimages - The preimages of the callstack items.
266
+ * @param isExecutionRequest - Whether the current callstack item should be considered a public fn execution request.
267
+ * @returns A corresponding PublicCallData object.
268
+ */
269
+ protected async getPublicCallData(
270
+ result: PublicExecutionResult,
271
+ preimages: Tuple<PublicCallStackItem, typeof PUBLIC_CALL_STACK_LENGTH>,
272
+ isExecutionRequest = false,
273
+ ) {
274
+ const bytecodeHash = await this.getBytecodeHash(result);
275
+ const callStackItem = await this.getPublicCallStackItem(result, isExecutionRequest);
276
+ const portalContractAddress = result.execution.callContext.portalContractAddress.toField();
277
+ const proof = await this.publicProver.getPublicCircuitProof(callStackItem.publicInputs);
278
+ return new PublicCallData(callStackItem, preimages, proof, portalContractAddress, bytecodeHash);
279
+ }
280
+ }
@@ -0,0 +1,158 @@
1
+ import {
2
+ CombinedHistoricTreeRoots,
3
+ Fr,
4
+ GlobalVariables,
5
+ NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
6
+ makeEmptyProof,
7
+ } from '@aztec/circuits.js';
8
+ import { P2P, P2PClientState } from '@aztec/p2p';
9
+ import { L1ToL2MessageSource, L2Block, L2BlockSource, MerkleTreeId, Tx, TxHash } from '@aztec/types';
10
+ import { MerkleTreeOperations, WorldStateRunningState, WorldStateSynchroniser } from '@aztec/world-state';
11
+ import { MockProxy, mock } from 'jest-mock-extended';
12
+ import times from 'lodash.times';
13
+ import { BlockBuilder } from '../block_builder/index.js';
14
+ import { L1Publisher, makeTx } from '../index.js';
15
+ import { makeEmptyProcessedTx, makeProcessedTx } from './processed_tx.js';
16
+ import { PublicProcessor, PublicProcessorFactory } from './public_processor.js';
17
+ import { Sequencer } from './sequencer.js';
18
+ import { GlobalVariableBuilder } from '../global_variable_builder/global_builder.js';
19
+
20
+ describe('sequencer', () => {
21
+ let publisher: MockProxy<L1Publisher>;
22
+ let globalVariableBuilder: MockProxy<GlobalVariableBuilder>;
23
+ let p2p: MockProxy<P2P>;
24
+ let worldState: MockProxy<WorldStateSynchroniser>;
25
+ let blockBuilder: MockProxy<BlockBuilder>;
26
+ let merkleTreeOps: MockProxy<MerkleTreeOperations>;
27
+ let publicProcessor: MockProxy<PublicProcessor>;
28
+ let l2BlockSource: MockProxy<L2BlockSource>;
29
+ let l1ToL2MessageSource: MockProxy<L1ToL2MessageSource>;
30
+ let publicProcessorFactory: MockProxy<PublicProcessorFactory>;
31
+
32
+ let lastBlockNumber: number;
33
+
34
+ let sequencer: TestSubject;
35
+
36
+ const chainId = Fr.ZERO;
37
+ const version = Fr.ZERO;
38
+
39
+ beforeEach(() => {
40
+ lastBlockNumber = 0;
41
+
42
+ publisher = mock<L1Publisher>();
43
+ globalVariableBuilder = mock<GlobalVariableBuilder>();
44
+ merkleTreeOps = mock<MerkleTreeOperations>();
45
+ blockBuilder = mock<BlockBuilder>();
46
+
47
+ p2p = mock<P2P>({
48
+ getStatus: () => Promise.resolve({ state: P2PClientState.IDLE, syncedToL2Block: lastBlockNumber }),
49
+ });
50
+
51
+ worldState = mock<WorldStateSynchroniser>({
52
+ getLatest: () => merkleTreeOps,
53
+ status: () => Promise.resolve({ state: WorldStateRunningState.IDLE, syncedToL2Block: lastBlockNumber }),
54
+ });
55
+
56
+ publicProcessor = mock<PublicProcessor>({
57
+ process: async txs => [await Promise.all(txs.map(tx => makeProcessedTx(tx))), []],
58
+ makeEmptyProcessedTx: () => makeEmptyProcessedTx(CombinedHistoricTreeRoots.empty(), chainId, version),
59
+ });
60
+
61
+ publicProcessorFactory = mock<PublicProcessorFactory>({
62
+ create: () => publicProcessor,
63
+ });
64
+
65
+ l2BlockSource = mock<L2BlockSource>({
66
+ getBlockHeight: () => Promise.resolve(lastBlockNumber),
67
+ });
68
+
69
+ l1ToL2MessageSource = mock<L1ToL2MessageSource>({
70
+ getPendingL1ToL2Messages: () => Promise.resolve(Array(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).fill(Fr.ZERO)),
71
+ });
72
+
73
+ sequencer = new TestSubject(
74
+ publisher,
75
+ globalVariableBuilder,
76
+ p2p,
77
+ worldState,
78
+ blockBuilder,
79
+ l2BlockSource,
80
+ l1ToL2MessageSource,
81
+ publicProcessorFactory,
82
+ {
83
+ chainId: Number(chainId.value),
84
+ version: Number(version.value),
85
+ },
86
+ );
87
+ });
88
+
89
+ it('builds a block out of a single tx', async () => {
90
+ const tx = makeTx();
91
+ const block = L2Block.random(lastBlockNumber + 1);
92
+ const proof = makeEmptyProof();
93
+
94
+ p2p.getTxs.mockResolvedValueOnce([tx]);
95
+ blockBuilder.buildL2Block.mockResolvedValueOnce([block, proof]);
96
+ publisher.processL2Block.mockResolvedValueOnce(true);
97
+ globalVariableBuilder.buildGlobalVariables.mockResolvedValueOnce(
98
+ new GlobalVariables(chainId, version, new Fr(lastBlockNumber + 1), Fr.ZERO),
99
+ );
100
+
101
+ await sequencer.initialSync();
102
+ await sequencer.work();
103
+
104
+ const expectedTxHashes = [...(await Tx.getHashes([tx])), ...times(3, () => TxHash.ZERO)];
105
+
106
+ expect(blockBuilder.buildL2Block).toHaveBeenCalledWith(
107
+ new GlobalVariables(chainId, version, new Fr(lastBlockNumber + 1), Fr.ZERO),
108
+ expectedTxHashes.map(hash => expect.objectContaining({ hash })),
109
+ Array(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).fill(new Fr(0n)),
110
+ );
111
+ expect(publisher.processL2Block).toHaveBeenCalledWith(block);
112
+ });
113
+
114
+ it('builds a block out of several txs rejecting double spends', async () => {
115
+ const txs = [makeTx(0x10000), makeTx(0x20000), makeTx(0x30000)];
116
+ const doubleSpendTx = txs[1];
117
+ const block = L2Block.random(lastBlockNumber + 1);
118
+ const proof = makeEmptyProof();
119
+
120
+ p2p.getTxs.mockResolvedValueOnce(txs);
121
+ blockBuilder.buildL2Block.mockResolvedValueOnce([block, proof]);
122
+ publisher.processL2Block.mockResolvedValueOnce(true);
123
+ globalVariableBuilder.buildGlobalVariables.mockResolvedValueOnce(
124
+ new GlobalVariables(chainId, version, new Fr(lastBlockNumber + 1), Fr.ZERO),
125
+ );
126
+
127
+ // We make a nullifier from tx1 a part of the nullifier tree, so it gets rejected as double spend
128
+ const doubleSpendNullifier = doubleSpendTx.data.end.newNullifiers[0].toBuffer();
129
+ merkleTreeOps.findLeafIndex.mockImplementation((treeId: MerkleTreeId, value: Buffer) => {
130
+ return Promise.resolve(
131
+ treeId === MerkleTreeId.NULLIFIER_TREE && value.equals(doubleSpendNullifier) ? 1n : undefined,
132
+ );
133
+ });
134
+
135
+ await sequencer.initialSync();
136
+ await sequencer.work();
137
+
138
+ const expectedTxHashes = [...(await Tx.getHashes([txs[0], txs[2]])), TxHash.ZERO, TxHash.ZERO];
139
+
140
+ expect(blockBuilder.buildL2Block).toHaveBeenCalledWith(
141
+ new GlobalVariables(chainId, version, new Fr(lastBlockNumber + 1), Fr.ZERO),
142
+ expectedTxHashes.map(hash => expect.objectContaining({ hash })),
143
+ Array(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).fill(new Fr(0n)),
144
+ );
145
+ expect(publisher.processL2Block).toHaveBeenCalledWith(block);
146
+ expect(p2p.deleteTxs).toHaveBeenCalledWith([await doubleSpendTx.getTxHash()]);
147
+ });
148
+ });
149
+
150
+ class TestSubject extends Sequencer {
151
+ public work() {
152
+ return super.work();
153
+ }
154
+
155
+ public initialSync(): Promise<void> {
156
+ return super.initialSync();
157
+ }
158
+ }