@aztec/aztec-node 0.1.0-alpha17 → 0.1.0-alpha21
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/.tsbuildinfo +1 -1
- package/dest/aztec-node/aztec-node.d.ts +11 -17
- package/dest/aztec-node/aztec-node.d.ts.map +1 -1
- package/dest/aztec-node/http-node.d.ts +15 -38
- package/dest/aztec-node/http-node.d.ts.map +1 -1
- package/dest/aztec-node/http-node.js +48 -74
- package/dest/aztec-node/http-node.test.d.ts +2 -0
- package/dest/aztec-node/http-node.test.d.ts.map +1 -0
- package/dest/aztec-node/http-node.test.js +322 -0
- package/dest/aztec-node/server.d.ts +14 -20
- package/dest/aztec-node/server.d.ts.map +1 -1
- package/dest/aztec-node/server.js +11 -18
- package/package.json +10 -10
- package/src/aztec-node/aztec-node.ts +11 -17
- package/src/aztec-node/http-node.test.ts +428 -0
- package/src/aztec-node/http-node.ts +53 -90
- package/src/aztec-node/server.ts +17 -24
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
import { AztecAddress, CircuitsWasm, Fr } from '@aztec/circuits.js';
|
|
2
|
+
import { randomBytes } from '@aztec/foundation/crypto';
|
|
3
|
+
import { Pedersen, SiblingPath } from '@aztec/merkle-tree';
|
|
4
|
+
import {
|
|
5
|
+
ContractData,
|
|
6
|
+
ContractPublicData,
|
|
7
|
+
L1ToL2Message,
|
|
8
|
+
L2Block,
|
|
9
|
+
L2BlockL2Logs,
|
|
10
|
+
LogType,
|
|
11
|
+
MerkleTreeId,
|
|
12
|
+
mockTx,
|
|
13
|
+
TxHash,
|
|
14
|
+
} from '@aztec/types';
|
|
15
|
+
import { jest } from '@jest/globals';
|
|
16
|
+
import { HttpNode } from './http-node.js';
|
|
17
|
+
|
|
18
|
+
const TEST_URL = 'http://aztec-node-url.com/';
|
|
19
|
+
|
|
20
|
+
const setFetchMock = (response: any, status = 200): void => {
|
|
21
|
+
global.fetch = jest
|
|
22
|
+
.fn<typeof global.fetch>()
|
|
23
|
+
.mockImplementation((_input: RequestInfo | URL, _init?: RequestInit | undefined) => {
|
|
24
|
+
return Promise.resolve({
|
|
25
|
+
status,
|
|
26
|
+
ok: true,
|
|
27
|
+
json: () => response,
|
|
28
|
+
arrayBuffer: () => response,
|
|
29
|
+
} as Response);
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
describe('HttpNode', () => {
|
|
34
|
+
let httpNode: HttpNode;
|
|
35
|
+
let pedersen: Pedersen;
|
|
36
|
+
|
|
37
|
+
beforeAll(async () => {
|
|
38
|
+
httpNode = new HttpNode(TEST_URL);
|
|
39
|
+
pedersen = new Pedersen(await CircuitsWasm.get());
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
afterEach(() => {
|
|
43
|
+
jest.clearAllMocks();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe('isReady', () => {
|
|
47
|
+
it.each([true, false])('should return %s when the node is ready', async () => {
|
|
48
|
+
const response = { isReady: true };
|
|
49
|
+
setFetchMock(response);
|
|
50
|
+
|
|
51
|
+
const result = await httpNode.isReady();
|
|
52
|
+
|
|
53
|
+
expect(fetch).toHaveBeenCalledWith(TEST_URL);
|
|
54
|
+
expect(result).toBe(true);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe('getBlocks', () => {
|
|
59
|
+
it('should fetch and parse blocks', async () => {
|
|
60
|
+
const block1 = L2Block.random(1);
|
|
61
|
+
const block2 = L2Block.random(2);
|
|
62
|
+
const response = {
|
|
63
|
+
blocks: [block1.encode(), block2.encode()],
|
|
64
|
+
};
|
|
65
|
+
setFetchMock(response);
|
|
66
|
+
|
|
67
|
+
const result = await httpNode.getBlocks(0, 3);
|
|
68
|
+
|
|
69
|
+
expect(fetch).toHaveBeenCalledWith(`${TEST_URL}get-blocks?from=0&take=3`);
|
|
70
|
+
expect(result).toEqual([block1, block2]);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should return an empty array if blocks are not available', async () => {
|
|
74
|
+
const response = { blocks: [] };
|
|
75
|
+
setFetchMock(response);
|
|
76
|
+
|
|
77
|
+
const result = await httpNode.getBlocks(0, 2);
|
|
78
|
+
|
|
79
|
+
expect(fetch).toHaveBeenCalledWith(`${TEST_URL}get-blocks?from=0&take=2`);
|
|
80
|
+
expect(result).toEqual([]);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe('getBlockHeight', () => {
|
|
85
|
+
it('should fetch and return the block height', async () => {
|
|
86
|
+
const response = { blockHeight: 100 };
|
|
87
|
+
setFetchMock(response);
|
|
88
|
+
|
|
89
|
+
const result = await httpNode.getBlockHeight();
|
|
90
|
+
|
|
91
|
+
expect(fetch).toHaveBeenCalledWith(`${TEST_URL}get-block-height`);
|
|
92
|
+
expect(result).toBe(100);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe('getVersion', () => {
|
|
97
|
+
it('should fetch and return the version', async () => {
|
|
98
|
+
const response = { version: 5 };
|
|
99
|
+
setFetchMock(response);
|
|
100
|
+
|
|
101
|
+
const result = await httpNode.getVersion();
|
|
102
|
+
|
|
103
|
+
expect(fetch).toHaveBeenCalledWith(`${TEST_URL}get-version`);
|
|
104
|
+
expect(result).toBe(5);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe('getChainId', () => {
|
|
109
|
+
it('should fetch and return the chain ID', async () => {
|
|
110
|
+
const response = { chainId: 1234 };
|
|
111
|
+
setFetchMock(response);
|
|
112
|
+
|
|
113
|
+
const result = await httpNode.getChainId();
|
|
114
|
+
|
|
115
|
+
expect(fetch).toHaveBeenCalledWith(`${TEST_URL}get-chain-id`);
|
|
116
|
+
expect(result).toBe(1234);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
describe('getContractData', () => {
|
|
121
|
+
it('should fetch and return contract public data', async () => {
|
|
122
|
+
const contractData = ContractPublicData.random();
|
|
123
|
+
const response = {
|
|
124
|
+
contractData: contractData.toBuffer(),
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
setFetchMock(response);
|
|
128
|
+
|
|
129
|
+
const result = await httpNode.getContractData(contractData.contractData.contractAddress);
|
|
130
|
+
|
|
131
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
132
|
+
`${TEST_URL}contract-data?address=${contractData.contractData.contractAddress.toString()}`,
|
|
133
|
+
);
|
|
134
|
+
expect(result).toEqual(contractData);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('should return undefined if contract data is not available', async () => {
|
|
138
|
+
const response = {
|
|
139
|
+
contractData: undefined,
|
|
140
|
+
};
|
|
141
|
+
setFetchMock(response);
|
|
142
|
+
|
|
143
|
+
const randomAddress = AztecAddress.random();
|
|
144
|
+
|
|
145
|
+
const result = await httpNode.getContractData(randomAddress);
|
|
146
|
+
|
|
147
|
+
expect(fetch).toHaveBeenCalledWith(`${TEST_URL}contract-data?address=${randomAddress.toString()}`);
|
|
148
|
+
expect(result).toEqual(undefined);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
describe('getLogs', () => {
|
|
153
|
+
it.each(['encrypted', 'unencrypted'])('should fetch and return %s logs', async logType => {
|
|
154
|
+
const processedLogType = logType === 'encrypted' ? LogType.ENCRYPTED : LogType.UNENCRYPTED;
|
|
155
|
+
|
|
156
|
+
const from = 0;
|
|
157
|
+
const take = 3;
|
|
158
|
+
const log1 = L2BlockL2Logs.random(2, 3, 4);
|
|
159
|
+
const log2 = L2BlockL2Logs.random(1, 5, 2);
|
|
160
|
+
const response = {
|
|
161
|
+
logs: [log1.toBuffer(), log2.toBuffer()],
|
|
162
|
+
};
|
|
163
|
+
setFetchMock(response);
|
|
164
|
+
|
|
165
|
+
const result = await httpNode.getLogs(from, take, processedLogType);
|
|
166
|
+
|
|
167
|
+
expect(fetch).toHaveBeenCalledWith(`${TEST_URL}get-logs?from=${from}&take=${take}&logType=${processedLogType}`);
|
|
168
|
+
expect(result).toEqual([log1, log2]);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it.each(['encrypted', 'unencrypted'])(
|
|
172
|
+
'should return an empty array if %s logs are not available',
|
|
173
|
+
async logType => {
|
|
174
|
+
const processedLogType = logType === 'encrypted' ? LogType.ENCRYPTED : LogType.UNENCRYPTED;
|
|
175
|
+
|
|
176
|
+
const from = 0;
|
|
177
|
+
const take = 2;
|
|
178
|
+
const response = {};
|
|
179
|
+
setFetchMock(response);
|
|
180
|
+
|
|
181
|
+
const result = await httpNode.getLogs(from, take, processedLogType);
|
|
182
|
+
|
|
183
|
+
expect(fetch).toHaveBeenCalledWith(`${TEST_URL}get-logs?from=${from}&take=${take}&logType=${processedLogType}`);
|
|
184
|
+
expect(result).toEqual([]);
|
|
185
|
+
},
|
|
186
|
+
);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
describe('getContractInfo', () => {
|
|
190
|
+
it('should fetch and return contract data', async () => {
|
|
191
|
+
const contractInfo = ContractData.random();
|
|
192
|
+
const response = {
|
|
193
|
+
contractInfo: contractInfo.toBuffer(),
|
|
194
|
+
};
|
|
195
|
+
setFetchMock(response);
|
|
196
|
+
|
|
197
|
+
const result = await httpNode.getContractInfo(contractInfo.contractAddress);
|
|
198
|
+
|
|
199
|
+
expect(fetch).toHaveBeenCalledWith(`${TEST_URL}contract-info?address=${contractInfo.contractAddress.toString()}`);
|
|
200
|
+
expect(result).toEqual(contractInfo);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('should return undefined if contract data is not available', async () => {
|
|
204
|
+
const response = {
|
|
205
|
+
contractInfo: undefined,
|
|
206
|
+
};
|
|
207
|
+
setFetchMock(response);
|
|
208
|
+
|
|
209
|
+
const randomAddress = AztecAddress.random();
|
|
210
|
+
|
|
211
|
+
const result = await httpNode.getContractInfo(randomAddress);
|
|
212
|
+
|
|
213
|
+
expect(fetch).toHaveBeenCalledWith(`${TEST_URL}contract-info?address=${randomAddress.toString()}`);
|
|
214
|
+
expect(result).toBeUndefined();
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
describe('sendTx', () => {
|
|
219
|
+
it('should submit a transaction to the p2p pool', async () => {
|
|
220
|
+
const tx = mockTx();
|
|
221
|
+
const irrelevantResponse = {};
|
|
222
|
+
setFetchMock(irrelevantResponse);
|
|
223
|
+
|
|
224
|
+
await httpNode.sendTx(tx);
|
|
225
|
+
|
|
226
|
+
const init: RequestInit = {
|
|
227
|
+
method: 'POST',
|
|
228
|
+
body: tx.toBuffer(),
|
|
229
|
+
};
|
|
230
|
+
const call = (fetch as jest.Mock).mock.calls[0] as any[];
|
|
231
|
+
expect(call[0].href).toBe(`${TEST_URL}tx`);
|
|
232
|
+
expect(call[1]).toStrictEqual(init);
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
describe('getPendingTxByHash', () => {
|
|
237
|
+
it('should fetch and return a pending tx', async () => {
|
|
238
|
+
const txHash = new TxHash(randomBytes(TxHash.SIZE));
|
|
239
|
+
const tx = mockTx();
|
|
240
|
+
const response = tx.toBuffer();
|
|
241
|
+
setFetchMock(response);
|
|
242
|
+
|
|
243
|
+
const result = await httpNode.getPendingTxByHash(txHash);
|
|
244
|
+
|
|
245
|
+
expect(fetch).toHaveBeenCalledWith(`${TEST_URL}get-pending-tx?hash=${txHash}`);
|
|
246
|
+
expect(result).toEqual(tx);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it('should return undefined if the pending tx does not exist', async () => {
|
|
250
|
+
const txHash = new TxHash(randomBytes(TxHash.SIZE));
|
|
251
|
+
const response = undefined;
|
|
252
|
+
setFetchMock(response, 404);
|
|
253
|
+
|
|
254
|
+
const result = await httpNode.getPendingTxByHash(txHash);
|
|
255
|
+
|
|
256
|
+
expect(fetch).toHaveBeenCalledWith(`${TEST_URL}get-pending-tx?hash=${txHash}`);
|
|
257
|
+
expect(result).toBeUndefined();
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
describe('findContractIndex', () => {
|
|
262
|
+
it('should fetch and return the index of the given contract', async () => {
|
|
263
|
+
const leafValue = Buffer.from('abc123', 'hex');
|
|
264
|
+
const index = '123456';
|
|
265
|
+
const response = { index };
|
|
266
|
+
setFetchMock(response);
|
|
267
|
+
|
|
268
|
+
const result = await httpNode.findContractIndex(leafValue);
|
|
269
|
+
|
|
270
|
+
expect(fetch).toHaveBeenCalledWith(`${TEST_URL}contract-index?leaf=${leafValue.toString('hex')}`);
|
|
271
|
+
expect(result).toBe(BigInt(index));
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it('should return undefined if the contract index is not found', async () => {
|
|
275
|
+
const leafValue = Buffer.from('def456', 'hex');
|
|
276
|
+
const response = {};
|
|
277
|
+
setFetchMock(response);
|
|
278
|
+
|
|
279
|
+
const result = await httpNode.findContractIndex(leafValue);
|
|
280
|
+
|
|
281
|
+
expect(fetch).toHaveBeenCalledWith(`${TEST_URL}contract-index?leaf=${leafValue.toString('hex')}`);
|
|
282
|
+
expect(result).toBeUndefined();
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
describe('getContractPath', () => {
|
|
287
|
+
it('should fetch and return the sibling path for the given leaf index', async () => {
|
|
288
|
+
const leafIndex = BigInt(123456);
|
|
289
|
+
const siblingPath = SiblingPath.ZERO(3, Buffer.alloc(32), pedersen);
|
|
290
|
+
const response = { path: siblingPath.toBuffer().toString('hex') };
|
|
291
|
+
setFetchMock(response);
|
|
292
|
+
|
|
293
|
+
const result = await httpNode.getContractPath(leafIndex);
|
|
294
|
+
|
|
295
|
+
expect(fetch).toHaveBeenCalledWith(`${TEST_URL}contract-path?leaf=${leafIndex.toString()}`);
|
|
296
|
+
expect(result).toEqual(siblingPath);
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
describe('findCommitmentIndex', () => {
|
|
301
|
+
it('should fetch and return the index of the given leaf', async () => {
|
|
302
|
+
const leafValue = Buffer.from('0123456789', 'hex');
|
|
303
|
+
const expectedIndex = BigInt(123);
|
|
304
|
+
const response = { index: expectedIndex.toString() };
|
|
305
|
+
setFetchMock(response);
|
|
306
|
+
|
|
307
|
+
const result = await httpNode.findCommitmentIndex(leafValue);
|
|
308
|
+
|
|
309
|
+
expect(fetch).toHaveBeenCalledWith(`${TEST_URL}commitment-index?leaf=${leafValue.toString('hex')}`);
|
|
310
|
+
expect(result).toBe(expectedIndex);
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
it('should return undefined if the commitment index is not found', async () => {
|
|
314
|
+
const leafValue = Buffer.from('def456', 'hex');
|
|
315
|
+
const response = {};
|
|
316
|
+
setFetchMock(response);
|
|
317
|
+
|
|
318
|
+
const result = await httpNode.findCommitmentIndex(leafValue);
|
|
319
|
+
|
|
320
|
+
expect(fetch).toHaveBeenCalledWith(`${TEST_URL}commitment-index?leaf=${leafValue.toString('hex')}`);
|
|
321
|
+
expect(result).toBeUndefined();
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
describe('getDataTreePath', () => {
|
|
326
|
+
it('should fetch and return the sibling path for the given leaf index', async () => {
|
|
327
|
+
const leafIndex = BigInt(123456);
|
|
328
|
+
const siblingPath = SiblingPath.ZERO(3, Buffer.alloc(32), pedersen);
|
|
329
|
+
const response = { path: siblingPath.toBuffer().toString('hex') };
|
|
330
|
+
setFetchMock(response);
|
|
331
|
+
|
|
332
|
+
const result = await httpNode.getDataTreePath(leafIndex);
|
|
333
|
+
|
|
334
|
+
expect(fetch).toHaveBeenCalledWith(`${TEST_URL}data-path?leaf=${leafIndex.toString()}`);
|
|
335
|
+
expect(result).toEqual(siblingPath);
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
describe('getL1ToL2MessageAndIndex', () => {
|
|
340
|
+
it('should fetch and return the L1 to L2 message and index for the given message key', async () => {
|
|
341
|
+
const messageKey = new Fr(789);
|
|
342
|
+
const expectedMessage = L1ToL2Message.random();
|
|
343
|
+
const expectedIndex = BigInt(123);
|
|
344
|
+
const response = { message: expectedMessage.toBuffer().toString('hex'), index: expectedIndex.toString() };
|
|
345
|
+
setFetchMock(response);
|
|
346
|
+
|
|
347
|
+
const result = await httpNode.getL1ToL2MessageAndIndex(messageKey);
|
|
348
|
+
|
|
349
|
+
expect(fetch).toHaveBeenCalledWith(`${TEST_URL}l1-l2-message?messageKey=${messageKey.toString()}`);
|
|
350
|
+
expect(result).toEqual({
|
|
351
|
+
message: expectedMessage,
|
|
352
|
+
index: expectedIndex,
|
|
353
|
+
});
|
|
354
|
+
});
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
describe('getL1ToL2MessagesTreePath', () => {
|
|
358
|
+
it('should fetch and return the sibling path for the given leaf index', async () => {
|
|
359
|
+
const leafIndex = BigInt(123456);
|
|
360
|
+
const siblingPath = SiblingPath.ZERO(3, Buffer.alloc(32), pedersen);
|
|
361
|
+
const response = { path: siblingPath.toBuffer().toString('hex') };
|
|
362
|
+
setFetchMock(response);
|
|
363
|
+
|
|
364
|
+
const result = await httpNode.getL1ToL2MessagesTreePath(leafIndex);
|
|
365
|
+
|
|
366
|
+
const url = `${TEST_URL}l1-l2-path?leaf=${leafIndex.toString()}`;
|
|
367
|
+
expect(fetch).toHaveBeenCalledWith(url);
|
|
368
|
+
expect(result).toEqual(siblingPath);
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
describe('getStorageAt', () => {
|
|
373
|
+
it('should fetch and return the storage value at the given contract slot', async () => {
|
|
374
|
+
const contractAddress = AztecAddress.random();
|
|
375
|
+
const slot = BigInt(789);
|
|
376
|
+
const storageValue = Buffer.from('0123456789', 'hex');
|
|
377
|
+
const response = { value: storageValue.toString('hex') };
|
|
378
|
+
setFetchMock(response);
|
|
379
|
+
|
|
380
|
+
const result = await httpNode.getStorageAt(contractAddress, slot);
|
|
381
|
+
|
|
382
|
+
const url = `${TEST_URL}storage-at?address=${contractAddress}&slot=${slot.toString()}`;
|
|
383
|
+
expect(fetch).toHaveBeenCalledWith(url);
|
|
384
|
+
expect(result).toEqual(storageValue);
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
it('should return undefined if the storage value is not found', async () => {
|
|
388
|
+
const contractAddress = AztecAddress.random();
|
|
389
|
+
const slot = BigInt(987);
|
|
390
|
+
const response = {};
|
|
391
|
+
setFetchMock(response);
|
|
392
|
+
|
|
393
|
+
const result = await httpNode.getStorageAt(contractAddress, slot);
|
|
394
|
+
|
|
395
|
+
const url = `${TEST_URL}storage-at?address=${contractAddress}&slot=${slot.toString()}`;
|
|
396
|
+
expect(fetch).toHaveBeenCalledWith(url);
|
|
397
|
+
expect(result).toBeUndefined();
|
|
398
|
+
});
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
describe('getTreeRoots', () => {
|
|
402
|
+
it('should fetch and return the current committed roots for the data trees', async () => {
|
|
403
|
+
const roots: Record<MerkleTreeId, Fr> = {
|
|
404
|
+
[MerkleTreeId.CONTRACT_TREE]: Fr.random(),
|
|
405
|
+
[MerkleTreeId.PRIVATE_DATA_TREE]: Fr.random(),
|
|
406
|
+
[MerkleTreeId.NULLIFIER_TREE]: Fr.random(),
|
|
407
|
+
[MerkleTreeId.PUBLIC_DATA_TREE]: Fr.random(),
|
|
408
|
+
[MerkleTreeId.L1_TO_L2_MESSAGES_TREE]: Fr.random(),
|
|
409
|
+
[MerkleTreeId.L1_TO_L2_MESSAGES_ROOTS_TREE]: Fr.random(),
|
|
410
|
+
[MerkleTreeId.CONTRACT_TREE_ROOTS_TREE]: Fr.random(),
|
|
411
|
+
[MerkleTreeId.PRIVATE_DATA_TREE_ROOTS_TREE]: Fr.random(),
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
const rootsInResponse: Record<MerkleTreeId, string> = Object.fromEntries(
|
|
415
|
+
Object.entries(roots).map(([key, value]) => [key, value.toString()]),
|
|
416
|
+
) as Record<MerkleTreeId, string>;
|
|
417
|
+
const response = { roots: rootsInResponse };
|
|
418
|
+
|
|
419
|
+
setFetchMock(response);
|
|
420
|
+
|
|
421
|
+
const result = await httpNode.getTreeRoots();
|
|
422
|
+
|
|
423
|
+
const url = `${TEST_URL}tree-roots`;
|
|
424
|
+
expect(fetch).toHaveBeenCalledWith(url);
|
|
425
|
+
expect(result).toEqual(roots);
|
|
426
|
+
});
|
|
427
|
+
});
|
|
428
|
+
});
|
|
@@ -3,76 +3,34 @@ import {
|
|
|
3
3
|
AztecAddress,
|
|
4
4
|
CONTRACT_TREE_HEIGHT,
|
|
5
5
|
Fr,
|
|
6
|
-
|
|
7
|
-
L1_TO_L2_MESSAGES_TREE_HEIGHT,
|
|
6
|
+
L1_TO_L2_MSG_TREE_HEIGHT,
|
|
8
7
|
PRIVATE_DATA_TREE_HEIGHT,
|
|
9
|
-
Proof,
|
|
10
|
-
PublicCallRequest,
|
|
11
8
|
} from '@aztec/circuits.js';
|
|
9
|
+
import { Logger, createLogger } from '@aztec/foundation/log';
|
|
12
10
|
import { SiblingPath } from '@aztec/merkle-tree';
|
|
13
11
|
import {
|
|
14
12
|
ContractData,
|
|
15
13
|
ContractPublicData,
|
|
16
|
-
EncodedContractFunction,
|
|
17
14
|
L1ToL2Message,
|
|
18
15
|
L1ToL2MessageAndIndex,
|
|
19
16
|
L2Block,
|
|
20
17
|
L2BlockL2Logs,
|
|
18
|
+
LogType,
|
|
21
19
|
MerkleTreeId,
|
|
22
20
|
Tx,
|
|
23
21
|
TxHash,
|
|
24
|
-
TxL2Logs,
|
|
25
22
|
} from '@aztec/types';
|
|
26
23
|
|
|
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
24
|
/**
|
|
70
25
|
* A Http client based implementation of Aztec Node.
|
|
71
26
|
*/
|
|
72
27
|
export class HttpNode implements AztecNode {
|
|
73
28
|
private baseUrl: string;
|
|
74
|
-
|
|
29
|
+
private log: Logger;
|
|
30
|
+
|
|
31
|
+
constructor(baseUrl: string, log = createLogger('aztec:http-node')) {
|
|
75
32
|
this.baseUrl = baseUrl.toString().replace(/\/$/, '');
|
|
33
|
+
this.log = log;
|
|
76
34
|
}
|
|
77
35
|
/**
|
|
78
36
|
* Method to determine if the node is ready to accept transactions.
|
|
@@ -120,7 +78,7 @@ export class HttpNode implements AztecNode {
|
|
|
120
78
|
* Method to fetch the version of the rollup the node is connected to.
|
|
121
79
|
* @returns The rollup version.
|
|
122
80
|
*/
|
|
123
|
-
public async getVersion(): Promise<
|
|
81
|
+
public async getVersion(): Promise<number> {
|
|
124
82
|
const url = new URL(`${this.baseUrl}/get-version`);
|
|
125
83
|
const response = await fetch(url.toString());
|
|
126
84
|
const respJson = await response.json();
|
|
@@ -131,7 +89,7 @@ export class HttpNode implements AztecNode {
|
|
|
131
89
|
* Method to fetch the chain id of the base-layer for the rollup.
|
|
132
90
|
* @returns The chain id.
|
|
133
91
|
*/
|
|
134
|
-
public async getChainId(): Promise<
|
|
92
|
+
public async getChainId(): Promise<number> {
|
|
135
93
|
const url = new URL(`${this.baseUrl}/get-chain-id`);
|
|
136
94
|
const response = await fetch(url.toString());
|
|
137
95
|
const respJson = await response.json();
|
|
@@ -148,55 +106,39 @@ export class HttpNode implements AztecNode {
|
|
|
148
106
|
const url = new URL(`${this.baseUrl}/contract-data`);
|
|
149
107
|
url.searchParams.append('address', contractAddress.toString());
|
|
150
108
|
const response = await (await fetch(url.toString())).json();
|
|
109
|
+
if (!response || !response.contractData) {
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
151
112
|
const contract = response.contractData as string;
|
|
152
113
|
return Promise.resolve(ContractPublicData.fromBuffer(Buffer.from(contract, 'hex')));
|
|
153
114
|
}
|
|
154
115
|
|
|
155
116
|
/**
|
|
156
|
-
* Gets the `take` amount of
|
|
157
|
-
* @param from - Number of the L2 block to which corresponds the first
|
|
158
|
-
* @param take - The number of
|
|
159
|
-
* @
|
|
117
|
+
* Gets the `take` amount of logs starting from `from`.
|
|
118
|
+
* @param from - Number of the L2 block to which corresponds the first logs to be returned.
|
|
119
|
+
* @param take - The number of logs to return.
|
|
120
|
+
* @param logType - Specifies whether to return encrypted or unencrypted logs.
|
|
121
|
+
* @returns The requested logs.
|
|
160
122
|
*/
|
|
161
|
-
public async
|
|
162
|
-
const url = new URL(`${this.baseUrl}/get-
|
|
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[];
|
|
123
|
+
public async getLogs(from: number, take: number, logType: LogType): Promise<L2BlockL2Logs[]> {
|
|
124
|
+
const url = new URL(`${this.baseUrl}/get-logs`);
|
|
169
125
|
|
|
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
126
|
url.searchParams.append('from', from.toString());
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
127
|
+
url.searchParams.append('take', take.toString());
|
|
128
|
+
url.searchParams.append('logType', logType.toString());
|
|
129
|
+
|
|
188
130
|
const response = await (await fetch(url.toString())).json();
|
|
189
|
-
const
|
|
131
|
+
const logs = response.logs as string[];
|
|
190
132
|
|
|
191
|
-
if (!
|
|
133
|
+
if (!logs) {
|
|
192
134
|
return Promise.resolve([]);
|
|
193
135
|
}
|
|
194
|
-
return Promise.resolve(
|
|
136
|
+
return Promise.resolve(logs.map(x => L2BlockL2Logs.fromBuffer(Buffer.from(x, 'hex'))));
|
|
195
137
|
}
|
|
196
138
|
|
|
197
139
|
/**
|
|
198
140
|
* Lookup the L2 contract info for this contract.
|
|
199
|
-
* Contains the ethereum portal address
|
|
141
|
+
* Contains the ethereum portal address.
|
|
200
142
|
* @param contractAddress - The contract data address.
|
|
201
143
|
* @returns The contract's address & portal address.
|
|
202
144
|
*/
|
|
@@ -204,6 +146,9 @@ export class HttpNode implements AztecNode {
|
|
|
204
146
|
const url = new URL(`${this.baseUrl}/contract-info`);
|
|
205
147
|
url.searchParams.append('address', contractAddress.toString());
|
|
206
148
|
const response = await (await fetch(url.toString())).json();
|
|
149
|
+
if (!response || !response.contractInfo) {
|
|
150
|
+
return undefined;
|
|
151
|
+
}
|
|
207
152
|
const contract = response.contractInfo as string;
|
|
208
153
|
return Promise.resolve(ContractData.fromBuffer(Buffer.from(contract, 'hex')));
|
|
209
154
|
}
|
|
@@ -214,10 +159,9 @@ export class HttpNode implements AztecNode {
|
|
|
214
159
|
*/
|
|
215
160
|
async sendTx(tx: Tx): Promise<void> {
|
|
216
161
|
const url = new URL(`${this.baseUrl}/tx`);
|
|
217
|
-
const json = txToJson(tx);
|
|
218
162
|
const init: RequestInit = {};
|
|
219
163
|
init['method'] = 'POST';
|
|
220
|
-
init['body'] =
|
|
164
|
+
init['body'] = tx.toBuffer();
|
|
221
165
|
await fetch(url, init);
|
|
222
166
|
}
|
|
223
167
|
|
|
@@ -237,8 +181,14 @@ export class HttpNode implements AztecNode {
|
|
|
237
181
|
async getPendingTxByHash(txHash: TxHash): Promise<Tx | undefined> {
|
|
238
182
|
const url = new URL(`${this.baseUrl}/get-pending-tx`);
|
|
239
183
|
url.searchParams.append('hash', txHash.toString());
|
|
240
|
-
const response = await
|
|
241
|
-
|
|
184
|
+
const response = await fetch(url.toString());
|
|
185
|
+
if (response.status === 404) {
|
|
186
|
+
this.log(`Tx ${txHash.toString()} not found`);
|
|
187
|
+
return undefined;
|
|
188
|
+
}
|
|
189
|
+
const txBuffer = Buffer.from(await (await fetch(url.toString())).arrayBuffer());
|
|
190
|
+
const tx = Tx.fromBuffer(txBuffer);
|
|
191
|
+
return Promise.resolve(tx);
|
|
242
192
|
}
|
|
243
193
|
|
|
244
194
|
/**
|
|
@@ -250,6 +200,9 @@ export class HttpNode implements AztecNode {
|
|
|
250
200
|
const url = new URL(`${this.baseUrl}/contract-index`);
|
|
251
201
|
url.searchParams.append('leaf', leafValue.toString('hex'));
|
|
252
202
|
const response = await (await fetch(url.toString())).json();
|
|
203
|
+
if (!response || !response.index) {
|
|
204
|
+
return undefined;
|
|
205
|
+
}
|
|
253
206
|
const index = response.index as string;
|
|
254
207
|
return Promise.resolve(BigInt(index));
|
|
255
208
|
}
|
|
@@ -276,6 +229,9 @@ export class HttpNode implements AztecNode {
|
|
|
276
229
|
const url = new URL(`${this.baseUrl}/commitment-index`);
|
|
277
230
|
url.searchParams.append('leaf', leafValue.toString('hex'));
|
|
278
231
|
const response = await (await fetch(url.toString())).json();
|
|
232
|
+
if (!response || !response.index) {
|
|
233
|
+
return undefined;
|
|
234
|
+
}
|
|
279
235
|
const index = response.index as string;
|
|
280
236
|
return Promise.resolve(BigInt(index));
|
|
281
237
|
}
|
|
@@ -313,7 +269,7 @@ export class HttpNode implements AztecNode {
|
|
|
313
269
|
* @param leafIndex - Index of the leaf in the tree.
|
|
314
270
|
* @returns The sibling path.
|
|
315
271
|
*/
|
|
316
|
-
async getL1ToL2MessagesTreePath(leafIndex: bigint): Promise<SiblingPath<typeof
|
|
272
|
+
async getL1ToL2MessagesTreePath(leafIndex: bigint): Promise<SiblingPath<typeof L1_TO_L2_MSG_TREE_HEIGHT>> {
|
|
317
273
|
const url = new URL(`${this.baseUrl}/l1-l2-path`);
|
|
318
274
|
url.searchParams.append('leaf', leafIndex.toString());
|
|
319
275
|
const response = await (await fetch(url.toString())).json();
|
|
@@ -333,6 +289,9 @@ export class HttpNode implements AztecNode {
|
|
|
333
289
|
url.searchParams.append('address', contract.toString());
|
|
334
290
|
url.searchParams.append('slot', slot.toString());
|
|
335
291
|
const response = await (await fetch(url.toString())).json();
|
|
292
|
+
if (!response || !response.value) {
|
|
293
|
+
return undefined;
|
|
294
|
+
}
|
|
336
295
|
const value = response.value as string;
|
|
337
296
|
return Promise.resolve(Buffer.from(value, 'hex'));
|
|
338
297
|
}
|
|
@@ -345,7 +304,11 @@ export class HttpNode implements AztecNode {
|
|
|
345
304
|
const url = new URL(`${this.baseUrl}/tree-roots`);
|
|
346
305
|
const response = await (await fetch(url.toString())).json();
|
|
347
306
|
|
|
348
|
-
const extractRoot = (treeId: MerkleTreeId) =>
|
|
307
|
+
const extractRoot = (treeId: MerkleTreeId) => {
|
|
308
|
+
// Buffer.from(...) returns an empty buffer when a hex string is prefixed with "0x"
|
|
309
|
+
const rootHexString = response.roots[treeId].replace(/^0x/, '');
|
|
310
|
+
return Fr.fromBuffer(Buffer.from(rootHexString, 'hex'));
|
|
311
|
+
};
|
|
349
312
|
|
|
350
313
|
return {
|
|
351
314
|
[MerkleTreeId.CONTRACT_TREE]: extractRoot(MerkleTreeId.CONTRACT_TREE),
|