@fizzyflow/endless-vector 0.0.1

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.
@@ -0,0 +1,433 @@
1
+ 'use strict'
2
+
3
+ import t, { end } from 'tap';
4
+ import { SuiMaster, SuiLocalTestValidator, SuiObject } from 'suidouble';
5
+ import { fileURLToPath } from 'url';
6
+ import path from 'path';
7
+ import { equalUint8Arrays, formatBytes, randomBytesOfLength } from './helpers.js';
8
+
9
+ import { EndlessVector } from '../index.js';
10
+ // import { get } from 'http';
11
+
12
+ const __filename = fileURLToPath(import.meta.url);
13
+ const __dirname = path.dirname(__filename);
14
+
15
+ const { test } = t;
16
+
17
+ let suiLocalTestValidator = null;
18
+
19
+ /** @type {?SuiMaster} */
20
+ let suiMaster = null;
21
+ let signAndExecuteTransaction = null;
22
+
23
+ let contract = null;
24
+
25
+ /** @type {?SuiObject} */
26
+ let endlessVectorRaw = null; // SuiObject instance
27
+ /** @type {?EndlessVector} */
28
+ let endlessVector = null; // EndlessVector instance
29
+
30
+ test('spawn local test node', async t => {
31
+ suiLocalTestValidator = await SuiLocalTestValidator.launch({ testFallbackEnabled: true, debug: true, });
32
+ t.ok(suiLocalTestValidator.active);
33
+ // SuiLocalTestValidator runs as signle instance. So you can't start it twice with static method
34
+ const suiLocalTestValidatorCopy = await SuiLocalTestValidator.launch();
35
+ t.equal(suiLocalTestValidator, suiLocalTestValidatorCopy);
36
+ });
37
+
38
+ test('init suiMaster and connect it to local test validator', async t => {
39
+ suiMaster = new SuiMaster({client: 'local', as: 'noname_tester', debug: true});
40
+ await suiMaster.initialize();
41
+ t.ok(suiMaster.address); // there should be some address
42
+ t.ok(`${suiMaster.address}`.indexOf('0x') === 0); // adress is string starting with '0x'
43
+
44
+ signAndExecuteTransaction = async (tx) => {
45
+ // function accepting Transaction and returning signed and submitted transaction digest
46
+ const results = await suiMaster.signAndExecuteTransaction({
47
+ transaction: tx,
48
+ });
49
+ return results.digest;
50
+ }
51
+ });
52
+
53
+ test('request sui from faucet', async t => {
54
+ const balanceBefore = await suiMaster.getBalance();
55
+ await suiMaster.requestSuiFromFaucet();
56
+ await suiMaster.requestSuiFromFaucet();
57
+ await suiMaster.requestSuiFromFaucet();
58
+ await suiMaster.requestSuiFromFaucet();
59
+ await suiMaster.requestSuiFromFaucet();
60
+ await suiMaster.requestSuiFromFaucet();
61
+ const balanceAfter = await suiMaster.getBalance();
62
+ t.ok(balanceAfter > balanceBefore);
63
+ });
64
+
65
+ test('attach a local package', async t => {
66
+ contract = suiMaster.addPackage({
67
+ path: path.join(__dirname, '../../move'),
68
+ });
69
+ await contract.build({ withUnpublishedDependencies: true, });
70
+ await contract.publish();
71
+ });
72
+
73
+
74
+
75
+
76
+ test('make a test EndlessVector and push single Uint8Array to it', async t => {
77
+ endlessVector = await EndlessVector.create({
78
+ suiClient: suiMaster.client, // instance of Sui SDK SuiClient
79
+ packageId: contract.id, // provide packageId and signAndExecuteTransaction to make EndlessVector writable
80
+ signAndExecuteTransaction: signAndExecuteTransaction,
81
+ });
82
+
83
+ t.ok(endlessVector);
84
+ t.ok(endlessVector.id);
85
+ t.ok(endlessVector.isWritable); // we provided packageId and signAndExecuteTransaction
86
+
87
+ await endlessVector.push(new Uint8Array([1,2,3]));
88
+
89
+ const getBack = await endlessVector.at(0);
90
+ t.ok(equalUint8Arrays(getBack, new Uint8Array([1,2,3])));
91
+
92
+ await endlessVector.initialize(); // endlessVector.at(0) calles initialize() internally, but we call it again to keep code clear
93
+
94
+ t.ok(endlessVector.length === 1);
95
+ t.ok(endlessVector.binaryLength === 3);
96
+ });
97
+
98
+ test('push Uint8Array larger than max_pure_argument_size to it', async t => {
99
+ const largeArray = randomBytesOfLength(30 * 1024); // 30KB
100
+ await endlessVector.push(largeArray);
101
+
102
+ const getBack = await endlessVector.at(1);
103
+ t.ok(equalUint8Arrays(getBack, largeArray));
104
+ t.ok(getBack.length === 30*1024);
105
+
106
+ t.ok(endlessVector.length === 2);
107
+ t.ok(endlessVector.binaryLength === 3 + 30*1024);
108
+ });
109
+
110
+ test('push Uint8Array larger than max_pure_argument_size to it', async t => {
111
+ const largeArray = randomBytesOfLength(120 * 1024); // 120KB
112
+ await endlessVector.push(largeArray);
113
+
114
+ const getBack = await endlessVector.at(2);
115
+ t.ok(equalUint8Arrays(getBack, largeArray));
116
+ t.ok(getBack.length === 120*1024);
117
+
118
+ t.ok(endlessVector.length === 3);
119
+ t.ok(endlessVector.binaryLength === 3 + 30*1024 + 120*1024);
120
+ });
121
+
122
+
123
+ test('throws an Error is trying to push too large Uint8Array ( split it on the higher level )', async t => {
124
+ const tooLargeSize = 120 * 1024 + 1; // 120KB + 1 byte
125
+ const largeArray = randomBytesOfLength(tooLargeSize);
126
+ await t.rejects(endlessVector.push(largeArray), Error, 'expected an Error to be thrown');
127
+ });
128
+
129
+ test('push few Uint8Array in a single transaction block', async t => {
130
+ const singleItemSize = 40 * 1024;
131
+ const largeArray1 = randomBytesOfLength(singleItemSize);
132
+ const largeArray2 = randomBytesOfLength(singleItemSize);
133
+ const largeArray3 = randomBytesOfLength(singleItemSize);
134
+ const tx = new suiMaster.Transaction();
135
+ await endlessVector.getPushTransaction(largeArray1, tx);
136
+ await endlessVector.getPushTransaction(largeArray2, tx);
137
+ await endlessVector.getPushTransaction(largeArray3, tx);
138
+
139
+ await suiMaster.signAndExecuteTransaction({
140
+ transaction: tx,
141
+ requestType: 'WaitForLocalExecution',
142
+ });
143
+
144
+ await endlessVector.reInitialize(); // force re-initialization to load new data
145
+ const getBack1 = await endlessVector.at(3);
146
+ const getBack2 = await endlessVector.at(4);
147
+ const getBack3 = await endlessVector.at(5);
148
+
149
+ t.ok(equalUint8Arrays(getBack1, largeArray1));
150
+ t.ok(getBack1.length === singleItemSize);
151
+
152
+ t.ok(equalUint8Arrays(getBack2, largeArray2));
153
+ t.ok(getBack2.length === singleItemSize);
154
+
155
+ t.ok(equalUint8Arrays(getBack3, largeArray3));
156
+ t.ok(getBack3.length === singleItemSize);
157
+
158
+ console.log(endlessVector.length);
159
+ console.log(endlessVector.binaryLength);
160
+ t.ok(endlessVector.length === 6);
161
+ t.ok(endlessVector.binaryLength === 3 + 30*1024 + 120*1024 + 3*singleItemSize);
162
+ });
163
+
164
+
165
+ test('test concat functionality', async t => {
166
+ // Create a second EndlessVector
167
+ const endlessVector2 = await EndlessVector.create({
168
+ suiClient: suiMaster.client, // instance of Sui SDK SuiClient
169
+ packageId: contract.id, // provide packageId and signAndExecuteTransaction to make EndlessVector writable
170
+ signAndExecuteTransaction: signAndExecuteTransaction,
171
+ });
172
+ t.ok(endlessVector2);
173
+ t.ok(endlessVector2.id);
174
+
175
+ // Push some data to the second vector
176
+ const data1 = new Uint8Array([10, 20, 30]);
177
+ const data2 = new Uint8Array([40, 50, 60]);
178
+ await endlessVector2.push(data1);
179
+ await endlessVector2.push(data2);
180
+
181
+ await endlessVector2.initialize();
182
+ t.ok(endlessVector2.length === 2);
183
+ t.ok(endlessVector2.binaryLength === 6);
184
+
185
+ // Get lengths before concat
186
+ await endlessVector.initialize();
187
+ const v1LengthBefore = endlessVector.length;
188
+ const v1BinaryLengthBefore = endlessVector.binaryLength;
189
+ const v2Length = endlessVector2.length;
190
+ const v2BinaryLength = endlessVector2.binaryLength;
191
+
192
+ console.log('v1 before concat:', { length: v1LengthBefore, binaryLength: v1BinaryLengthBefore });
193
+ console.log('v2 before concat:', { length: v2Length, binaryLength: v2BinaryLength });
194
+
195
+ // Concat the second vector into the first
196
+ // Both of these work - passing ID string or EndlessVector instance:
197
+ // await endlessVector.concat(endlessVector2.id);
198
+ await endlessVector.concat(endlessVector2); // Passing EndlessVector instance
199
+
200
+ // Verify the first vector now contains both vectors' data
201
+ await endlessVector.initialize();
202
+ console.log('v1 after concat:', { length: endlessVector.length, binaryLength: endlessVector.binaryLength });
203
+
204
+ t.ok(endlessVector.length === v1LengthBefore + v2Length, 'length should be sum of both vectors');
205
+ t.ok(endlessVector.binaryLength === v1BinaryLengthBefore + v2BinaryLength, 'binary length should be sum of both vectors');
206
+
207
+ // Verify we can access the concatenated data
208
+ const getBackFromV2_1 = await endlessVector.at(v1LengthBefore);
209
+ const getBackFromV2_2 = await endlessVector.at(v1LengthBefore + 1);
210
+
211
+ t.ok(equalUint8Arrays(getBackFromV2_1, data1), 'first item from v2 should be accessible in v1');
212
+ t.ok(equalUint8Arrays(getBackFromV2_2, data2), 'second item from v2 should be accessible in v1');
213
+ });
214
+
215
+
216
+ test('test concat with array (append)', async t => {
217
+ // Create three new EndlessVectors
218
+ const endlessVector3 = await EndlessVector.create({
219
+ suiClient: suiMaster.client, // instance of Sui SDK SuiClient
220
+ packageId: contract.id, // provide packageId and signAndExecuteTransaction to make EndlessVector writable
221
+ signAndExecuteTransaction: signAndExecuteTransaction,
222
+ });
223
+ const endlessVector4 = await EndlessVector.create({
224
+ suiClient: suiMaster.client, // instance of Sui SDK SuiClient
225
+ packageId: contract.id, // provide packageId and signAndExecuteTransaction to make EndlessVector writable
226
+ signAndExecuteTransaction: signAndExecuteTransaction,
227
+ });
228
+ const endlessVector5 = await EndlessVector.create({
229
+ suiClient: suiMaster.client, // instance of Sui SDK SuiClient
230
+ packageId: contract.id, // provide packageId and signAndExecuteTransaction to make EndlessVector writable
231
+ signAndExecuteTransaction: signAndExecuteTransaction,
232
+ });
233
+
234
+ // Push data to all vectors
235
+ const data3 = new Uint8Array([100, 101, 102]);
236
+ const data4 = new Uint8Array([200, 201, 202]);
237
+ const data5 = new Uint8Array([300, 301, 302]);
238
+
239
+ await endlessVector3.push(data3);
240
+ await endlessVector4.push(data4);
241
+ await endlessVector5.push(data5);
242
+
243
+ await endlessVector3.initialize();
244
+ await endlessVector4.initialize();
245
+ await endlessVector5.initialize();
246
+
247
+ const v3Length = endlessVector3.length;
248
+ const v3BinaryLength = endlessVector3.binaryLength;
249
+ const v4Length = endlessVector4.length;
250
+ const v4BinaryLength = endlessVector4.binaryLength;
251
+ const v5Length = endlessVector5.length;
252
+ const v5BinaryLength = endlessVector5.binaryLength;
253
+
254
+ console.log('Before append:', { v3Length, v4Length, v5Length });
255
+
256
+ // Concat with array (append) - passing array of EndlessVector instances
257
+ await endlessVector3.concat([endlessVector4, endlessVector5]);
258
+
259
+ await endlessVector3.initialize();
260
+ console.log('After append:', { length: endlessVector3.length, binaryLength: endlessVector3.binaryLength });
261
+
262
+ t.ok(endlessVector3.length === v3Length + v4Length + v5Length, 'length should be sum of all vectors');
263
+ t.ok(endlessVector3.binaryLength === v3BinaryLength + v4BinaryLength + v5BinaryLength, 'binary length should be sum of all vectors');
264
+
265
+ // Verify data integrity
266
+ const retrieved3 = await endlessVector3.at(0);
267
+ const retrieved4 = await endlessVector3.at(1);
268
+ const retrieved5 = await endlessVector3.at(2);
269
+
270
+ t.ok(equalUint8Arrays(retrieved3, data3), 'v3 data should match');
271
+ t.ok(equalUint8Arrays(retrieved4, data4), 'v4 data should match');
272
+ t.ok(equalUint8Arrays(retrieved5, data5), 'v5 data should match');
273
+ });
274
+
275
+
276
+ test('test concat large EndlessVectors', async t => {
277
+ // Create three new EndlessVectors
278
+ const endlessVectorMain = await EndlessVector.create({
279
+ suiClient: suiMaster.client, // instance of Sui SDK SuiClient
280
+ packageId: contract.id, // provide packageId and signAndExecuteTransaction to make EndlessVector writable
281
+ signAndExecuteTransaction: signAndExecuteTransaction,
282
+ });
283
+ const endlessVectorSecond = await EndlessVector.create({
284
+ suiClient: suiMaster.client, // instance of Sui SDK SuiClient
285
+ packageId: contract.id, // provide packageId and signAndExecuteTransaction to make EndlessVector writable
286
+ signAndExecuteTransaction: signAndExecuteTransaction,
287
+ });
288
+
289
+ const data = [
290
+ ];
291
+
292
+ for (let i = 0; i < 33; i++) {
293
+ let toMain = randomBytesOfLength(0 + Math.floor(1024 + 50 * 1024 * Math.random())); // 1KB - 50KB
294
+ if (Math.random() < 0.2) {
295
+ toMain = new Uint8Array([]); // 20% chance to push empty array
296
+ }
297
+
298
+ data.push(toMain);
299
+ }
300
+
301
+ for (const item of data) {
302
+ await endlessVectorMain.push(item);
303
+ }
304
+
305
+ for (let i = 0; i < 33; i++) {
306
+ let toSecond = randomBytesOfLength(0 + Math.floor(1024 + 50 * 1024 * Math.random())); // 1KB - 50KB
307
+ if (Math.random() < 0.2) {
308
+ toSecond = new Uint8Array([]); // 20% chance to push empty array
309
+ }
310
+
311
+ await endlessVectorSecond.push(toSecond);
312
+ data.push(toSecond);
313
+ }
314
+
315
+ await endlessVectorMain.concat(endlessVectorSecond);
316
+
317
+ await endlessVectorMain.initialize();
318
+
319
+ t.ok(endlessVectorMain.length === data.length, 'length should be sum of both vectors');
320
+
321
+ // Verify data integrity
322
+ for (let i = 0; i < data.length; i++) {
323
+ const retrieved = await endlessVectorMain.at(i);
324
+ t.ok(equalUint8Arrays(retrieved, data[i]), `data at index ${i} should match`);
325
+ }
326
+ });
327
+
328
+
329
+ test('test parallel creation, push, and append of 6 vectors', async t => {
330
+ const vectorCount = 6;
331
+
332
+ // Split gas into at least N coins to use them in parallel transactions
333
+ const splitTx = new suiMaster.Transaction();
334
+ for (let i = 0; i < vectorCount; i++) {
335
+ let coin = splitTx.splitCoins(splitTx.gas, [splitTx.pure.u64(BigInt(1000000000))]);
336
+ splitTx.transferObjects([coin], splitTx.pure.address(suiMaster.address));
337
+ }
338
+ await suiMaster.signAndExecuteTransaction({
339
+ transaction: splitTx,
340
+ requestType: 'WaitForLocalExecution',
341
+ });
342
+
343
+ // Get gas coins and prepare inputs for tx.setGasPayment(...);
344
+ const gasCoins = await suiMaster.client.getCoins({
345
+ owner: suiMaster.address,
346
+ coinType: '0x2::sui::SUI',
347
+ });
348
+ const gasCoinInputs = gasCoins.data.map((c) => {
349
+ return {
350
+ objectId: c.coinObjectId,
351
+ digest: c.digest,
352
+ version: c.version,
353
+ };
354
+ });
355
+
356
+ t.ok(gasCoinInputs.length >= vectorCount, `should have at least ${vectorCount} gas coins for parallel vector creation.`);
357
+
358
+ // Prepare test data for each vector
359
+ const testData = [];
360
+ for (let i = 0; i < vectorCount; i++) {
361
+ testData.push([randomBytesOfLength(1024), new Uint8Array([0, 1, i])]);
362
+ }
363
+
364
+ // Create N EndlessVectors in parallel using static create method
365
+ const createPromises = [];
366
+ for (let i = 0; i < vectorCount; i++) {
367
+ createPromises.push(
368
+ EndlessVector.create({
369
+ suiClient: suiMaster.client,
370
+ packageId: contract.id,
371
+ items: testData[i] ? testData[i] : [],
372
+ gasCoin: gasCoinInputs[i],
373
+ signAndExecuteTransaction: async (tx) => {
374
+ console.log(`Creating vector ${i}...`);
375
+ try {
376
+ const results = await suiMaster.signAndExecuteTransaction({
377
+ transaction: tx,
378
+ });
379
+ return results.digest;
380
+ } catch (e) {
381
+ console.error(`Error creating vector ${i}:`, e);
382
+ }
383
+ },
384
+ })
385
+ );
386
+ }
387
+
388
+ const vectors = await Promise.all(createPromises);
389
+ vectors.forEach((v, i) => {
390
+ console.log(`Created vector ${i} with id: ${v.id}`);
391
+ });
392
+
393
+ t.ok(vectors.length === vectorCount, `should have created ${vectorCount} vectors`);
394
+
395
+ const ids = {};
396
+ vectors.forEach((v, i) => {
397
+ t.ok(v.id, `vector ${i} should have an id`);
398
+ t.ok(!ids[v.id], `vector id ${v.id} should be unique`);
399
+ ids[v.id] = i;
400
+ });
401
+
402
+ const mainVector = vectors[0];
403
+ // append other vectors to the first one
404
+ const vectorsToAppend = vectors.slice(1);
405
+ await mainVector.concat(vectorsToAppend);
406
+
407
+ // Verify the data after concatenation
408
+ await mainVector.initialize();
409
+
410
+ // Each vector has 2 items, so total should be vectorCount * 2
411
+ const expectedLength = vectorCount * 2;
412
+ t.ok(mainVector.length === expectedLength, `mainVector should have ${expectedLength} items, got ${mainVector.length}`);
413
+
414
+ console.log(`Verifying ${expectedLength} items in concatenated vector...`);
415
+
416
+ // Verify each item matches the original testData
417
+ for (let vectorIdx = 0; vectorIdx < vectorCount; vectorIdx++) {
418
+ for (let itemIdx = 0; itemIdx < testData[vectorIdx].length; itemIdx++) {
419
+ const globalIdx = vectorIdx * testData[vectorIdx].length + itemIdx;
420
+ const retrieved = await mainVector.at(globalIdx);
421
+ const expected = testData[vectorIdx][itemIdx];
422
+
423
+ t.ok(
424
+ equalUint8Arrays(retrieved, expected),
425
+ `Item ${globalIdx} (vector ${vectorIdx}, item ${itemIdx}) should match`
426
+ );
427
+ }
428
+ }
429
+ });
430
+
431
+ test('stops local test node', async t => {
432
+ await SuiLocalTestValidator.stop();
433
+ });
@@ -0,0 +1,32 @@
1
+ import crypto from 'crypto';
2
+
3
+ function equalUint8Arrays(a, b) {
4
+ if (a.length !== b.length) return false;
5
+ for (let i = 0; i < a.length; i++) {
6
+ if (a[i] !== b[i]) return false;
7
+ }
8
+ return true;
9
+ }
10
+
11
+ function formatBytes(bytes, decimals = 2) {
12
+ if (bytes === 0) return '0 Bytes';
13
+
14
+ const k = 1024;
15
+ const dm = decimals < 0 ? 0 : decimals;
16
+ const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];
17
+
18
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
19
+ const size = parseFloat((bytes / Math.pow(k, i)).toFixed(dm));
20
+
21
+ return `${size} ${sizes[i]}`;
22
+ }
23
+
24
+ function randomBytesOfLength(length) {
25
+ return new Uint8Array(crypto.randomBytes(length));
26
+ }
27
+
28
+ export {
29
+ equalUint8Arrays,
30
+ formatBytes,
31
+ randomBytesOfLength
32
+ };