@aztec/aztec-node 0.1.0-alpha13
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/.dockerignore +4 -0
- package/.eslintrc.cjs +1 -0
- package/.tsbuildinfo +1 -0
- package/README.md +15 -0
- package/dest/aztec-node/aztec-node.d.ts +132 -0
- package/dest/aztec-node/aztec-node.d.ts.map +1 -0
- package/dest/aztec-node/aztec-node.js +2 -0
- package/dest/aztec-node/config.d.ts +13 -0
- package/dest/aztec-node/config.d.ts.map +1 -0
- package/dest/aztec-node/config.js +16 -0
- package/dest/aztec-node/http-node.d.ts +152 -0
- package/dest/aztec-node/http-node.d.ts.map +1 -0
- package/dest/aztec-node/http-node.js +306 -0
- package/dest/aztec-node/server.d.ts +161 -0
- package/dest/aztec-node/server.d.ts.map +1 -0
- package/dest/aztec-node/server.js +239 -0
- package/dest/index.d.ts +5 -0
- package/dest/index.d.ts.map +1 -0
- package/dest/index.js +5 -0
- package/package.json +19 -0
- package/src/aztec-node/aztec-node.ts +159 -0
- package/src/aztec-node/config.ts +22 -0
- package/src/aztec-node/http-node.ts +361 -0
- package/src/aztec-node/server.ts +321 -0
- package/src/index.ts +4 -0
- package/tsconfig.json +38 -0
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import { AztecNode } from '@aztec/aztec-node';
|
|
2
|
+
import {
|
|
3
|
+
AztecAddress,
|
|
4
|
+
CONTRACT_TREE_HEIGHT,
|
|
5
|
+
Fr,
|
|
6
|
+
KernelCircuitPublicInputs,
|
|
7
|
+
L1_TO_L2_MESSAGES_TREE_HEIGHT,
|
|
8
|
+
PRIVATE_DATA_TREE_HEIGHT,
|
|
9
|
+
Proof,
|
|
10
|
+
PublicCallRequest,
|
|
11
|
+
} from '@aztec/circuits.js';
|
|
12
|
+
import { SiblingPath } from '@aztec/merkle-tree';
|
|
13
|
+
import {
|
|
14
|
+
ContractData,
|
|
15
|
+
ContractPublicData,
|
|
16
|
+
EncodedContractFunction,
|
|
17
|
+
L1ToL2Message,
|
|
18
|
+
L1ToL2MessageAndIndex,
|
|
19
|
+
L2Block,
|
|
20
|
+
L2BlockL2Logs,
|
|
21
|
+
MerkleTreeId,
|
|
22
|
+
Tx,
|
|
23
|
+
TxHash,
|
|
24
|
+
TxL2Logs,
|
|
25
|
+
} from '@aztec/types';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Serialises a transaction to JSON representation.
|
|
29
|
+
* @param tx - The transaction to serialise.
|
|
30
|
+
* @returns The serialsied transaction.
|
|
31
|
+
*/
|
|
32
|
+
export function txToJson(tx: Tx) {
|
|
33
|
+
return {
|
|
34
|
+
data: tx.data?.toBuffer().toString('hex'),
|
|
35
|
+
encryptedLogs: tx.encryptedLogs?.toBuffer().toString('hex'),
|
|
36
|
+
unencryptedLogs: tx.unencryptedLogs?.toBuffer().toString('hex'),
|
|
37
|
+
proof: tx.proof?.toBuffer().toString('hex'),
|
|
38
|
+
newContractPublicFunctions: tx.newContractPublicFunctions?.map(f => f.toBuffer().toString('hex')) ?? [],
|
|
39
|
+
enqueuedPublicFunctions: tx.enqueuedPublicFunctionCalls?.map(f => f.toBuffer().toString('hex')) ?? [],
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Deserialises a transaction from JSON.
|
|
45
|
+
* @param json - The JSON representation of the transaction.
|
|
46
|
+
* @returns The deserialised transaction.
|
|
47
|
+
*/
|
|
48
|
+
export function txFromJson(json: any) {
|
|
49
|
+
const publicInputs = KernelCircuitPublicInputs.fromBuffer(Buffer.from(json.data, 'hex'));
|
|
50
|
+
const encryptedLogs = TxL2Logs.fromBuffer(Buffer.from(json.encryptedLogs, 'hex'));
|
|
51
|
+
const unencryptedLogs = TxL2Logs.fromBuffer(Buffer.from(json.unencryptedLogs, 'hex'));
|
|
52
|
+
const proof = Buffer.from(json.proof, 'hex');
|
|
53
|
+
const newContractPublicFunctions = json.newContractPublicFunctions?.length
|
|
54
|
+
? json.newContractPublicFunctions.map((x: string) => EncodedContractFunction.fromBuffer(Buffer.from(x, 'hex')))
|
|
55
|
+
: [];
|
|
56
|
+
const enqueuedPublicFunctions = json.enqueuedPublicFunctions?.length
|
|
57
|
+
? json.enqueuedPublicFunctions.map((x: string) => PublicCallRequest.fromBuffer(Buffer.from(x, 'hex')))
|
|
58
|
+
: [];
|
|
59
|
+
return new Tx(
|
|
60
|
+
publicInputs,
|
|
61
|
+
Proof.fromBuffer(proof),
|
|
62
|
+
encryptedLogs,
|
|
63
|
+
unencryptedLogs,
|
|
64
|
+
newContractPublicFunctions,
|
|
65
|
+
enqueuedPublicFunctions,
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* A Http client based implementation of Aztec Node.
|
|
71
|
+
*/
|
|
72
|
+
export class HttpNode implements AztecNode {
|
|
73
|
+
private baseUrl: string;
|
|
74
|
+
constructor(baseUrl: string) {
|
|
75
|
+
this.baseUrl = baseUrl.toString().replace(/\/$/, '');
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Method to determine if the node is ready to accept transactions.
|
|
79
|
+
* @returns - Flag indicating the readiness for tx submission.
|
|
80
|
+
*/
|
|
81
|
+
public async isReady(): Promise<boolean> {
|
|
82
|
+
const url = new URL(this.baseUrl);
|
|
83
|
+
const response = await fetch(url.toString());
|
|
84
|
+
const respJson = await response.json();
|
|
85
|
+
return respJson.isReady;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Method to request blocks. Will attempt to return all requested blocks but will return only those available.
|
|
90
|
+
* @param from - The start of the range of blocks to return.
|
|
91
|
+
* @param take - The number of blocks desired.
|
|
92
|
+
* @returns The blocks requested.
|
|
93
|
+
*/
|
|
94
|
+
async getBlocks(from: number, take: number): Promise<L2Block[]> {
|
|
95
|
+
const url = new URL(`${this.baseUrl}/get-blocks`);
|
|
96
|
+
url.searchParams.append('from', from.toString());
|
|
97
|
+
if (take !== undefined) {
|
|
98
|
+
url.searchParams.append('take', take.toString());
|
|
99
|
+
}
|
|
100
|
+
const response = await (await fetch(url.toString())).json();
|
|
101
|
+
const blocks = response.blocks as string[];
|
|
102
|
+
if (!blocks) {
|
|
103
|
+
return Promise.resolve([]);
|
|
104
|
+
}
|
|
105
|
+
return Promise.resolve(blocks.map(x => L2Block.decode(Buffer.from(x, 'hex'))));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Method to fetch the current block height.
|
|
110
|
+
* @returns The block height as a number.
|
|
111
|
+
*/
|
|
112
|
+
async getBlockHeight(): Promise<number> {
|
|
113
|
+
const url = new URL(`${this.baseUrl}/get-block-height`);
|
|
114
|
+
const response = await fetch(url.toString());
|
|
115
|
+
const respJson = await response.json();
|
|
116
|
+
return respJson.blockHeight;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Method to fetch the version of the rollup the node is connected to.
|
|
121
|
+
* @returns The rollup version.
|
|
122
|
+
*/
|
|
123
|
+
public async getVersion(): Promise<Fr> {
|
|
124
|
+
const url = new URL(`${this.baseUrl}/get-version`);
|
|
125
|
+
const response = await fetch(url.toString());
|
|
126
|
+
const respJson = await response.json();
|
|
127
|
+
return respJson.version;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Method to fetch the chain id of the base-layer for the rollup.
|
|
132
|
+
* @returns The chain id.
|
|
133
|
+
*/
|
|
134
|
+
public async getChainId(): Promise<Fr> {
|
|
135
|
+
const url = new URL(`${this.baseUrl}/get-chain-id`);
|
|
136
|
+
const response = await fetch(url.toString());
|
|
137
|
+
const respJson = await response.json();
|
|
138
|
+
return respJson.chainId;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Lookup the L2 contract data for this contract.
|
|
143
|
+
* Contains the ethereum portal address and bytecode.
|
|
144
|
+
* @param contractAddress - The contract data address.
|
|
145
|
+
* @returns The complete contract data including portal address & bytecode (if we didn't throw an error).
|
|
146
|
+
*/
|
|
147
|
+
async getContractData(contractAddress: AztecAddress): Promise<ContractPublicData | undefined> {
|
|
148
|
+
const url = new URL(`${this.baseUrl}/contract-data`);
|
|
149
|
+
url.searchParams.append('address', contractAddress.toString());
|
|
150
|
+
const response = await (await fetch(url.toString())).json();
|
|
151
|
+
const contract = response.contractData as string;
|
|
152
|
+
return Promise.resolve(ContractPublicData.fromBuffer(Buffer.from(contract, 'hex')));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Gets the `take` amount of encrypted logs starting from `from`.
|
|
157
|
+
* @param from - Number of the L2 block to which corresponds the first encrypted logs to be returned.
|
|
158
|
+
* @param take - The number of encrypted logs to return.
|
|
159
|
+
* @returns The requested encrypted logs.
|
|
160
|
+
*/
|
|
161
|
+
public async getEncryptedLogs(from: number, take: number): Promise<L2BlockL2Logs[]> {
|
|
162
|
+
const url = new URL(`${this.baseUrl}/get-encrypted-logs`);
|
|
163
|
+
url.searchParams.append('from', from.toString());
|
|
164
|
+
if (take !== undefined) {
|
|
165
|
+
url.searchParams.append('take', take.toString());
|
|
166
|
+
}
|
|
167
|
+
const response = await (await fetch(url.toString())).json();
|
|
168
|
+
const encryptedLogs = response.encryptedLogs as string[];
|
|
169
|
+
|
|
170
|
+
if (!encryptedLogs) {
|
|
171
|
+
return Promise.resolve([]);
|
|
172
|
+
}
|
|
173
|
+
return Promise.resolve(encryptedLogs.map(x => L2BlockL2Logs.fromBuffer(Buffer.from(x, 'hex'))));
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Gets the `take` amount of unencrypted logs starting from `from`.
|
|
178
|
+
* @param from - Number of the L2 block to which corresponds the first unencrypted logs to be returned.
|
|
179
|
+
* @param take - The number of unencrypted logs to return.
|
|
180
|
+
* @returns The requested unencrypted logs.
|
|
181
|
+
*/
|
|
182
|
+
public async getUnencryptedLogs(from: number, take: number): Promise<L2BlockL2Logs[]> {
|
|
183
|
+
const url = new URL(`${this.baseUrl}/get-unencrypted-logs`);
|
|
184
|
+
url.searchParams.append('from', from.toString());
|
|
185
|
+
if (take !== undefined) {
|
|
186
|
+
url.searchParams.append('take', take.toString());
|
|
187
|
+
}
|
|
188
|
+
const response = await (await fetch(url.toString())).json();
|
|
189
|
+
const unencryptedLogs = response.unencryptedLogs as string[];
|
|
190
|
+
|
|
191
|
+
if (!unencryptedLogs) {
|
|
192
|
+
return Promise.resolve([]);
|
|
193
|
+
}
|
|
194
|
+
return Promise.resolve(unencryptedLogs.map(x => L2BlockL2Logs.fromBuffer(Buffer.from(x, 'hex'))));
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Lookup the L2 contract info for this contract.
|
|
199
|
+
* Contains the ethereum portal address .
|
|
200
|
+
* @param contractAddress - The contract data address.
|
|
201
|
+
* @returns The contract's address & portal address.
|
|
202
|
+
*/
|
|
203
|
+
async getContractInfo(contractAddress: AztecAddress): Promise<ContractData | undefined> {
|
|
204
|
+
const url = new URL(`${this.baseUrl}/contract-info`);
|
|
205
|
+
url.searchParams.append('address', contractAddress.toString());
|
|
206
|
+
const response = await (await fetch(url.toString())).json();
|
|
207
|
+
const contract = response.contractInfo as string;
|
|
208
|
+
return Promise.resolve(ContractData.fromBuffer(Buffer.from(contract, 'hex')));
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Method to submit a transaction to the p2p pool.
|
|
213
|
+
* @param tx - The transaction to be submitted.
|
|
214
|
+
*/
|
|
215
|
+
async sendTx(tx: Tx): Promise<void> {
|
|
216
|
+
const url = new URL(`${this.baseUrl}/tx`);
|
|
217
|
+
const json = txToJson(tx);
|
|
218
|
+
const init: RequestInit = {};
|
|
219
|
+
init['method'] = 'POST';
|
|
220
|
+
init['body'] = JSON.stringify(json);
|
|
221
|
+
await fetch(url, init);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Method to retrieve pending txs.
|
|
226
|
+
* @returns - The pending txs.
|
|
227
|
+
*/
|
|
228
|
+
getPendingTxs(): Promise<Tx[]> {
|
|
229
|
+
return Promise.resolve([]);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Method to retrieve a single pending tx.
|
|
234
|
+
* @param txHash - The transaction hash to return.
|
|
235
|
+
* @returns - The pending tx if it exists.
|
|
236
|
+
*/
|
|
237
|
+
async getPendingTxByHash(txHash: TxHash): Promise<Tx | undefined> {
|
|
238
|
+
const url = new URL(`${this.baseUrl}/get-pending-tx`);
|
|
239
|
+
url.searchParams.append('hash', txHash.toString());
|
|
240
|
+
const response = await (await fetch(url.toString())).json();
|
|
241
|
+
return Promise.resolve(txFromJson(response));
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Find the index of the given contract.
|
|
246
|
+
* @param leafValue - The value to search for.
|
|
247
|
+
* @returns The index of the given leaf in the contracts tree or undefined if not found.
|
|
248
|
+
*/
|
|
249
|
+
async findContractIndex(leafValue: Buffer): Promise<bigint | undefined> {
|
|
250
|
+
const url = new URL(`${this.baseUrl}/contract-index`);
|
|
251
|
+
url.searchParams.append('leaf', leafValue.toString('hex'));
|
|
252
|
+
const response = await (await fetch(url.toString())).json();
|
|
253
|
+
const index = response.index as string;
|
|
254
|
+
return Promise.resolve(BigInt(index));
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Returns the sibling path for the given index in the contract tree.
|
|
259
|
+
* @param leafIndex - The index of the leaf for which the sibling path is required.
|
|
260
|
+
* @returns The sibling path for the leaf index.
|
|
261
|
+
*/
|
|
262
|
+
async getContractPath(leafIndex: bigint): Promise<SiblingPath<typeof CONTRACT_TREE_HEIGHT>> {
|
|
263
|
+
const url = new URL(`${this.baseUrl}/contract-path`);
|
|
264
|
+
url.searchParams.append('leaf', leafIndex.toString());
|
|
265
|
+
const response = await (await fetch(url.toString())).json();
|
|
266
|
+
const path = response.path as string;
|
|
267
|
+
return Promise.resolve(SiblingPath.fromString(path));
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Find the index of the given piece of data.
|
|
272
|
+
* @param leafValue - The value to search for.
|
|
273
|
+
* @returns The index of the given leaf in the data tree or undefined if not found.
|
|
274
|
+
*/
|
|
275
|
+
async findCommitmentIndex(leafValue: Buffer): Promise<bigint | undefined> {
|
|
276
|
+
const url = new URL(`${this.baseUrl}/commitment-index`);
|
|
277
|
+
url.searchParams.append('leaf', leafValue.toString('hex'));
|
|
278
|
+
const response = await (await fetch(url.toString())).json();
|
|
279
|
+
const index = response.index as string;
|
|
280
|
+
return Promise.resolve(BigInt(index));
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Returns the sibling path for the given index in the data tree.
|
|
285
|
+
* @param leafIndex - The index of the leaf for which the sibling path is required.
|
|
286
|
+
* @returns The sibling path for the leaf index.
|
|
287
|
+
*/
|
|
288
|
+
async getDataTreePath(leafIndex: bigint): Promise<SiblingPath<typeof PRIVATE_DATA_TREE_HEIGHT>> {
|
|
289
|
+
const url = new URL(`${this.baseUrl}/data-path`);
|
|
290
|
+
url.searchParams.append('leaf', leafIndex.toString());
|
|
291
|
+
const response = await (await fetch(url.toString())).json();
|
|
292
|
+
const path = response.path as string;
|
|
293
|
+
return Promise.resolve(SiblingPath.fromString(path));
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Gets a consumed/confirmed L1 to L2 message for the given message key and its index in the merkle tree.
|
|
298
|
+
* @param messageKey - The message key.
|
|
299
|
+
* @returns the message (or throws if not found)
|
|
300
|
+
*/
|
|
301
|
+
async getL1ToL2MessageAndIndex(messageKey: Fr): Promise<L1ToL2MessageAndIndex> {
|
|
302
|
+
const url = new URL(`${this.baseUrl}/l1-l2-message`);
|
|
303
|
+
url.searchParams.append('messageKey', messageKey.toString());
|
|
304
|
+
const response = await (await fetch(url.toString())).json();
|
|
305
|
+
return Promise.resolve({
|
|
306
|
+
message: L1ToL2Message.fromBuffer(Buffer.from(response.message as string, 'hex')),
|
|
307
|
+
index: BigInt(response.index as string),
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Returns the sibling path for a leaf in the committed l1 to l2 data tree.
|
|
313
|
+
* @param leafIndex - Index of the leaf in the tree.
|
|
314
|
+
* @returns The sibling path.
|
|
315
|
+
*/
|
|
316
|
+
async getL1ToL2MessagesTreePath(leafIndex: bigint): Promise<SiblingPath<typeof L1_TO_L2_MESSAGES_TREE_HEIGHT>> {
|
|
317
|
+
const url = new URL(`${this.baseUrl}/l1-l2-path`);
|
|
318
|
+
url.searchParams.append('leaf', leafIndex.toString());
|
|
319
|
+
const response = await (await fetch(url.toString())).json();
|
|
320
|
+
const path = response.path as string;
|
|
321
|
+
return Promise.resolve(SiblingPath.fromString(path));
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Gets the storage value at the given contract slot.
|
|
326
|
+
* @param contract - Address of the contract to query.
|
|
327
|
+
* @param slot - Slot to query.
|
|
328
|
+
* @returns Storage value at the given contract slot (or undefined if not found).
|
|
329
|
+
* Note: Aztec's version of `eth_getStorageAt`.
|
|
330
|
+
*/
|
|
331
|
+
async getStorageAt(contract: AztecAddress, slot: bigint): Promise<Buffer | undefined> {
|
|
332
|
+
const url = new URL(`${this.baseUrl}/storage-at`);
|
|
333
|
+
url.searchParams.append('address', contract.toString());
|
|
334
|
+
url.searchParams.append('slot', slot.toString());
|
|
335
|
+
const response = await (await fetch(url.toString())).json();
|
|
336
|
+
const value = response.value as string;
|
|
337
|
+
return Promise.resolve(Buffer.from(value, 'hex'));
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Returns the current committed roots for the data trees.
|
|
342
|
+
* @returns The current committed roots for the data trees.
|
|
343
|
+
*/
|
|
344
|
+
async getTreeRoots(): Promise<Record<MerkleTreeId, Fr>> {
|
|
345
|
+
const url = new URL(`${this.baseUrl}/tree-roots`);
|
|
346
|
+
const response = await (await fetch(url.toString())).json();
|
|
347
|
+
|
|
348
|
+
const extractRoot = (treeId: MerkleTreeId) => Fr.fromBuffer(Buffer.from(response.roots[`${treeId}`], 'hex'));
|
|
349
|
+
|
|
350
|
+
return {
|
|
351
|
+
[MerkleTreeId.CONTRACT_TREE]: extractRoot(MerkleTreeId.CONTRACT_TREE),
|
|
352
|
+
[MerkleTreeId.PRIVATE_DATA_TREE]: extractRoot(MerkleTreeId.PRIVATE_DATA_TREE),
|
|
353
|
+
[MerkleTreeId.NULLIFIER_TREE]: extractRoot(MerkleTreeId.NULLIFIER_TREE),
|
|
354
|
+
[MerkleTreeId.PUBLIC_DATA_TREE]: extractRoot(MerkleTreeId.PUBLIC_DATA_TREE),
|
|
355
|
+
[MerkleTreeId.L1_TO_L2_MESSAGES_TREE]: extractRoot(MerkleTreeId.L1_TO_L2_MESSAGES_TREE),
|
|
356
|
+
[MerkleTreeId.L1_TO_L2_MESSAGES_ROOTS_TREE]: extractRoot(MerkleTreeId.L1_TO_L2_MESSAGES_ROOTS_TREE),
|
|
357
|
+
[MerkleTreeId.CONTRACT_TREE_ROOTS_TREE]: extractRoot(MerkleTreeId.CONTRACT_TREE_ROOTS_TREE),
|
|
358
|
+
[MerkleTreeId.PRIVATE_DATA_TREE_ROOTS_TREE]: extractRoot(MerkleTreeId.PRIVATE_DATA_TREE_ROOTS_TREE),
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
}
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import { Archiver } from '@aztec/archiver';
|
|
2
|
+
import {
|
|
3
|
+
CONTRACT_TREE_HEIGHT,
|
|
4
|
+
CircuitsWasm,
|
|
5
|
+
Fr,
|
|
6
|
+
L1_TO_L2_MESSAGES_TREE_HEIGHT,
|
|
7
|
+
PRIVATE_DATA_TREE_HEIGHT,
|
|
8
|
+
} from '@aztec/circuits.js';
|
|
9
|
+
import { AztecAddress } from '@aztec/foundation/aztec-address';
|
|
10
|
+
import { SiblingPath } from '@aztec/merkle-tree';
|
|
11
|
+
import { InMemoryTxPool, P2P, createP2PClient } from '@aztec/p2p';
|
|
12
|
+
import { SequencerClient, getCombinedHistoricTreeRoots } from '@aztec/sequencer-client';
|
|
13
|
+
import {
|
|
14
|
+
ContractData,
|
|
15
|
+
ContractDataSource,
|
|
16
|
+
ContractPublicData,
|
|
17
|
+
L1ToL2MessageAndIndex,
|
|
18
|
+
L1ToL2MessageSource,
|
|
19
|
+
L2Block,
|
|
20
|
+
L2BlockL2Logs,
|
|
21
|
+
L2BlockSource,
|
|
22
|
+
L2LogsSource,
|
|
23
|
+
MerkleTreeId,
|
|
24
|
+
Tx,
|
|
25
|
+
TxHash,
|
|
26
|
+
} from '@aztec/types';
|
|
27
|
+
import {
|
|
28
|
+
MerkleTrees,
|
|
29
|
+
ServerWorldStateSynchroniser,
|
|
30
|
+
WorldStateSynchroniser,
|
|
31
|
+
computePublicDataTreeLeafIndex,
|
|
32
|
+
} from '@aztec/world-state';
|
|
33
|
+
import { default as levelup } from 'levelup';
|
|
34
|
+
import { MemDown, default as memdown } from 'memdown';
|
|
35
|
+
import { AztecNode } from './aztec-node.js';
|
|
36
|
+
import { AztecNodeConfig } from './config.js';
|
|
37
|
+
|
|
38
|
+
export const createMemDown = () => (memdown as any)() as MemDown<any, any>;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The aztec node.
|
|
42
|
+
*/
|
|
43
|
+
export class AztecNodeService implements AztecNode {
|
|
44
|
+
constructor(
|
|
45
|
+
protected p2pClient: P2P,
|
|
46
|
+
protected blockSource: L2BlockSource,
|
|
47
|
+
protected encryptedLogsSource: L2LogsSource,
|
|
48
|
+
protected unencryptedLogsSource: L2LogsSource,
|
|
49
|
+
protected contractDataSource: ContractDataSource,
|
|
50
|
+
protected l1ToL2MessageSource: L1ToL2MessageSource,
|
|
51
|
+
protected merkleTreeDB: MerkleTrees,
|
|
52
|
+
protected worldStateSynchroniser: WorldStateSynchroniser,
|
|
53
|
+
protected sequencer: SequencerClient,
|
|
54
|
+
protected chainId: Fr,
|
|
55
|
+
protected version: Fr,
|
|
56
|
+
) {}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Initialises the Aztec Node, wait for component to sync.
|
|
60
|
+
* @param config - The configuration to be used by the aztec node.
|
|
61
|
+
* @returns - A fully synced Aztec Node for use in development/testing.
|
|
62
|
+
*/
|
|
63
|
+
public static async createAndSync(config: AztecNodeConfig) {
|
|
64
|
+
// first create and sync the archiver
|
|
65
|
+
const archiver = await Archiver.createAndSync(config);
|
|
66
|
+
|
|
67
|
+
// we idenfity the P2P transaction protocol by using the rollup contract address.
|
|
68
|
+
// this may well change in future
|
|
69
|
+
config.transactionProtocol = `/aztec/tx/${config.rollupContract.toString()}`;
|
|
70
|
+
|
|
71
|
+
// create the tx pool and the p2p client, which will need the l2 block source
|
|
72
|
+
const p2pClient = await createP2PClient(config, new InMemoryTxPool(), archiver);
|
|
73
|
+
|
|
74
|
+
// now create the merkle trees and the world state syncher
|
|
75
|
+
const merkleTreeDB = await MerkleTrees.new(levelup(createMemDown()), await CircuitsWasm.get());
|
|
76
|
+
const worldStateSynchroniser = new ServerWorldStateSynchroniser(merkleTreeDB, archiver);
|
|
77
|
+
|
|
78
|
+
// start both and wait for them to sync from the block source
|
|
79
|
+
await Promise.all([p2pClient.start(), worldStateSynchroniser.start()]);
|
|
80
|
+
|
|
81
|
+
// now create the sequencer
|
|
82
|
+
const sequencer = await SequencerClient.new(
|
|
83
|
+
config,
|
|
84
|
+
p2pClient,
|
|
85
|
+
worldStateSynchroniser,
|
|
86
|
+
archiver,
|
|
87
|
+
archiver,
|
|
88
|
+
archiver,
|
|
89
|
+
);
|
|
90
|
+
return new AztecNodeService(
|
|
91
|
+
p2pClient,
|
|
92
|
+
archiver,
|
|
93
|
+
archiver,
|
|
94
|
+
archiver,
|
|
95
|
+
archiver,
|
|
96
|
+
archiver,
|
|
97
|
+
merkleTreeDB,
|
|
98
|
+
worldStateSynchroniser,
|
|
99
|
+
sequencer,
|
|
100
|
+
new Fr(config.chainId),
|
|
101
|
+
new Fr(config.version),
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Method to determine if the node is ready to accept transactions.
|
|
107
|
+
* @returns - Flag indicating the readiness for tx submission.
|
|
108
|
+
*/
|
|
109
|
+
public async isReady() {
|
|
110
|
+
return (await this.p2pClient.isReady()) ?? false;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Method to request blocks. Will attempt to return all requested blocks but will return only those available.
|
|
115
|
+
* @param from - The start of the range of blocks to return.
|
|
116
|
+
* @param take - The number of blocks desired.
|
|
117
|
+
* @returns The blocks requested.
|
|
118
|
+
*/
|
|
119
|
+
public async getBlocks(from: number, take: number): Promise<L2Block[]> {
|
|
120
|
+
return (await this.blockSource.getL2Blocks(from, take)) ?? [];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Method to fetch the current block height.
|
|
125
|
+
* @returns The block height as a number.
|
|
126
|
+
*/
|
|
127
|
+
public async getBlockHeight(): Promise<number> {
|
|
128
|
+
return await this.blockSource.getBlockHeight();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Method to fetch the version of the rollup the node is connected to.
|
|
133
|
+
* @returns The rollup version.
|
|
134
|
+
*/
|
|
135
|
+
public getVersion(): Promise<Fr> {
|
|
136
|
+
return Promise.resolve(this.version);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Method to fetch the chain id of the base-layer for the rollup.
|
|
141
|
+
* @returns The chain id.
|
|
142
|
+
*/
|
|
143
|
+
public getChainId(): Promise<Fr> {
|
|
144
|
+
return Promise.resolve(this.chainId);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Lookup the L2 contract data for this contract.
|
|
149
|
+
* Contains the ethereum portal address and bytecode.
|
|
150
|
+
* @param contractAddress - The contract data address.
|
|
151
|
+
* @returns The complete contract data including portal address & bytecode (if we didn't throw an error).
|
|
152
|
+
*/
|
|
153
|
+
public async getContractData(contractAddress: AztecAddress): Promise<ContractPublicData | undefined> {
|
|
154
|
+
return await this.contractDataSource.getL2ContractPublicData(contractAddress);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Lookup the L2 contract info for this contract.
|
|
159
|
+
* Contains the ethereum portal address .
|
|
160
|
+
* @param contractAddress - The contract data address.
|
|
161
|
+
* @returns The contract's address & portal address.
|
|
162
|
+
*/
|
|
163
|
+
public async getContractInfo(contractAddress: AztecAddress): Promise<ContractData | undefined> {
|
|
164
|
+
return await this.contractDataSource.getL2ContractInfo(contractAddress);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Gets the `take` amount of encrypted logs starting from `from`.
|
|
169
|
+
* @param from - Number of the L2 block to which corresponds the first encrypted log to be returned.
|
|
170
|
+
* @param take - The number of encrypted logs to return.
|
|
171
|
+
* @returns The requested encrypted logs.
|
|
172
|
+
*/
|
|
173
|
+
public getEncryptedLogs(from: number, take: number): Promise<L2BlockL2Logs[]> {
|
|
174
|
+
return this.encryptedLogsSource.getEncryptedLogs(from, take);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Gets the `take` amount of unencrypted logs starting from `from`.
|
|
179
|
+
* @param from - Number of the L2 block to which corresponds the first unencrypted log to be returned.
|
|
180
|
+
* @param take - The number of unencrypted logs to return.
|
|
181
|
+
* @returns The requested unencrypted logs.
|
|
182
|
+
*/
|
|
183
|
+
public getUnencryptedLogs(from: number, take: number): Promise<L2BlockL2Logs[]> {
|
|
184
|
+
return this.unencryptedLogsSource.getUnencryptedLogs(from, take);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Method to submit a transaction to the p2p pool.
|
|
189
|
+
* @param tx - The transaction to be submitted.
|
|
190
|
+
*/
|
|
191
|
+
public async sendTx(tx: Tx) {
|
|
192
|
+
// TODO: Patch tx to inject historic tree roots until the private kernel circuit supplies this value
|
|
193
|
+
if (tx.data.constants.historicTreeRoots.privateHistoricTreeRoots.isEmpty()) {
|
|
194
|
+
tx.data.constants.historicTreeRoots = await getCombinedHistoricTreeRoots(this.merkleTreeDB.asLatest());
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
await this.p2pClient!.sendTx(tx);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Method to stop the aztec node.
|
|
202
|
+
*/
|
|
203
|
+
public async stop() {
|
|
204
|
+
await this.sequencer.stop();
|
|
205
|
+
await this.p2pClient.stop();
|
|
206
|
+
await this.worldStateSynchroniser.stop();
|
|
207
|
+
await this.merkleTreeDB.stop();
|
|
208
|
+
await this.blockSource.stop();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Method to retrieve pending txs.
|
|
213
|
+
* @returns - The pending txs.
|
|
214
|
+
*/
|
|
215
|
+
public async getPendingTxs() {
|
|
216
|
+
return await this.p2pClient!.getTxs();
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Method to retrieve a single pending tx.
|
|
221
|
+
* @param txHash - The transaction hash to return.
|
|
222
|
+
* @returns - The pending tx if it exists.
|
|
223
|
+
*/
|
|
224
|
+
public async getPendingTxByHash(txHash: TxHash) {
|
|
225
|
+
return await this.p2pClient!.getTxByhash(txHash);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Find the index of the given contract.
|
|
230
|
+
* @param leafValue - The value to search for.
|
|
231
|
+
* @returns The index of the given leaf in the contracts tree or undefined if not found.
|
|
232
|
+
*/
|
|
233
|
+
public findContractIndex(leafValue: Buffer): Promise<bigint | undefined> {
|
|
234
|
+
return this.merkleTreeDB.findLeafIndex(MerkleTreeId.CONTRACT_TREE, leafValue, false);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Returns the sibling path for the given index in the contract tree.
|
|
239
|
+
* @param leafIndex - The index of the leaf for which the sibling path is required.
|
|
240
|
+
* @returns The sibling path for the leaf index.
|
|
241
|
+
*/
|
|
242
|
+
public getContractPath(leafIndex: bigint): Promise<SiblingPath<typeof CONTRACT_TREE_HEIGHT>> {
|
|
243
|
+
return this.merkleTreeDB.getSiblingPath(MerkleTreeId.CONTRACT_TREE, leafIndex, false);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Find the index of the given commitment.
|
|
248
|
+
* @param leafValue - The value to search for.
|
|
249
|
+
* @returns The index of the given leaf in the private data tree or undefined if not found.
|
|
250
|
+
*/
|
|
251
|
+
public findCommitmentIndex(leafValue: Buffer): Promise<bigint | undefined> {
|
|
252
|
+
return this.merkleTreeDB.findLeafIndex(MerkleTreeId.PRIVATE_DATA_TREE, leafValue, false);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Returns the sibling path for the given index in the data tree.
|
|
257
|
+
* @param leafIndex - The index of the leaf for which the sibling path is required.
|
|
258
|
+
* @returns The sibling path for the leaf index.
|
|
259
|
+
*/
|
|
260
|
+
public getDataTreePath(leafIndex: bigint): Promise<SiblingPath<typeof PRIVATE_DATA_TREE_HEIGHT>> {
|
|
261
|
+
return this.merkleTreeDB.getSiblingPath(MerkleTreeId.PRIVATE_DATA_TREE, leafIndex, false);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Gets a confirmed/consumed L1 to L2 message for the given message key
|
|
266
|
+
* and its index in the merkle tree.
|
|
267
|
+
* @param messageKey - The message key.
|
|
268
|
+
* @returns The map containing the message and index.
|
|
269
|
+
*/
|
|
270
|
+
public async getL1ToL2MessageAndIndex(messageKey: Fr): Promise<L1ToL2MessageAndIndex> {
|
|
271
|
+
// todo: #697 - make this one lookup.
|
|
272
|
+
const message = await this.l1ToL2MessageSource.getConfirmedL1ToL2Message(messageKey);
|
|
273
|
+
const index = (await this.merkleTreeDB.findLeafIndex(
|
|
274
|
+
MerkleTreeId.L1_TO_L2_MESSAGES_TREE,
|
|
275
|
+
messageKey.toBuffer(),
|
|
276
|
+
false,
|
|
277
|
+
))!;
|
|
278
|
+
return Promise.resolve({ message, index });
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Returns the sibling path for a leaf in the committed l1 to l2 data tree.
|
|
283
|
+
* @param leafIndex - Index of the leaf in the tree.
|
|
284
|
+
* @returns The sibling path.
|
|
285
|
+
*/
|
|
286
|
+
public getL1ToL2MessagesTreePath(leafIndex: bigint): Promise<SiblingPath<typeof L1_TO_L2_MESSAGES_TREE_HEIGHT>> {
|
|
287
|
+
return this.merkleTreeDB.getSiblingPath(MerkleTreeId.L1_TO_L2_MESSAGES_TREE, leafIndex, false);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Gets the storage value at the given contract slot.
|
|
292
|
+
* @param contract - Address of the contract to query.
|
|
293
|
+
* @param slot - Slot to query.
|
|
294
|
+
* @returns Storage value at the given contract slot (or undefined if not found).
|
|
295
|
+
* Note: Aztec's version of `eth_getStorageAt`.
|
|
296
|
+
*/
|
|
297
|
+
public async getStorageAt(contract: AztecAddress, slot: bigint): Promise<Buffer | undefined> {
|
|
298
|
+
const leafIndex = computePublicDataTreeLeafIndex(contract, new Fr(slot), await CircuitsWasm.get());
|
|
299
|
+
return this.merkleTreeDB.getLeafValue(MerkleTreeId.PUBLIC_DATA_TREE, leafIndex, false);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Returns the current committed roots for the data trees.
|
|
304
|
+
* @returns The current committed roots for the data trees.
|
|
305
|
+
*/
|
|
306
|
+
public async getTreeRoots(): Promise<Record<MerkleTreeId, Fr>> {
|
|
307
|
+
const getTreeRoot = async (id: MerkleTreeId) =>
|
|
308
|
+
Fr.fromBuffer((await this.merkleTreeDB.getTreeInfo(id, false)).root);
|
|
309
|
+
|
|
310
|
+
return {
|
|
311
|
+
[MerkleTreeId.CONTRACT_TREE]: await getTreeRoot(MerkleTreeId.CONTRACT_TREE),
|
|
312
|
+
[MerkleTreeId.PRIVATE_DATA_TREE]: await getTreeRoot(MerkleTreeId.PRIVATE_DATA_TREE),
|
|
313
|
+
[MerkleTreeId.NULLIFIER_TREE]: await getTreeRoot(MerkleTreeId.NULLIFIER_TREE),
|
|
314
|
+
[MerkleTreeId.PUBLIC_DATA_TREE]: await getTreeRoot(MerkleTreeId.PUBLIC_DATA_TREE),
|
|
315
|
+
[MerkleTreeId.L1_TO_L2_MESSAGES_TREE]: await getTreeRoot(MerkleTreeId.L1_TO_L2_MESSAGES_TREE),
|
|
316
|
+
[MerkleTreeId.L1_TO_L2_MESSAGES_ROOTS_TREE]: await getTreeRoot(MerkleTreeId.L1_TO_L2_MESSAGES_ROOTS_TREE),
|
|
317
|
+
[MerkleTreeId.CONTRACT_TREE_ROOTS_TREE]: await getTreeRoot(MerkleTreeId.CONTRACT_TREE_ROOTS_TREE),
|
|
318
|
+
[MerkleTreeId.PRIVATE_DATA_TREE_ROOTS_TREE]: await getTreeRoot(MerkleTreeId.PRIVATE_DATA_TREE_ROOTS_TREE),
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
}
|
package/src/index.ts
ADDED