@rosen-bridge/tx-pot 0.1.0 → 1.0.0

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.
@@ -1,6 +1,11 @@
1
1
  import { Repository } from 'typeorm';
2
2
  import { mockDataSource } from '../db/dataSource.mock';
3
- import { TransactionEntity, TransactionStatus } from '../../lib';
3
+ import {
4
+ CallbackFunction,
5
+ TransactionEntity,
6
+ TransactionStatus,
7
+ ValidatorFunction,
8
+ } from '../../lib';
4
9
  import { TestTxPot } from './TestTxPot';
5
10
  import * as testData from './testData';
6
11
  import { TestPotChainManager } from '../network/TestPotChainManager';
@@ -25,6 +30,87 @@ describe('TxPot', () => {
25
30
  vi.useRealTimers();
26
31
  });
27
32
 
33
+ describe('unregisterValidator', () => {
34
+ /**
35
+ * @target TxPot.unregisterValidator should remove registered validator successfully
36
+ * @dependencies
37
+ * @scenario
38
+ * - register a validator function
39
+ * - run test
40
+ * - check registered validators
41
+ * @expected
42
+ * - there should be no validator
43
+ */
44
+ it('should remove registered validator successfully', async () => {
45
+ const mockedValidator = async (tx: TransactionEntity) => true;
46
+ txPot.registerValidator('chain', 'txType', 'id', mockedValidator);
47
+
48
+ txPot.unregisterValidator('chain', 'txType', 'id');
49
+
50
+ const validatorsMap: Map<
51
+ string,
52
+ Map<string, Map<string, ValidatorFunction>>
53
+ > = (txPot as any).validators;
54
+ expect(validatorsMap.get('chain')?.get('txType')?.size).toEqual(0);
55
+ });
56
+ });
57
+
58
+ describe('unregisterSubmitValidator', () => {
59
+ /**
60
+ * @target TxPot.unregisterSubmitValidator should remove registered submit validator successfully
61
+ * @dependencies
62
+ * @scenario
63
+ * - register a validator function
64
+ * - run test
65
+ * - check registered submit validators
66
+ * @expected
67
+ * - there should be no validator
68
+ */
69
+ it('should remove registered submit validator successfully', async () => {
70
+ const mockedValidator = async (tx: TransactionEntity) => true;
71
+ txPot.registerSubmitValidator('chain', 'id', mockedValidator);
72
+
73
+ txPot.unregisterSubmitValidator('chain', 'id');
74
+
75
+ const validatorsMap: Map<string, Map<string, ValidatorFunction>> = (
76
+ txPot as any
77
+ ).submissionAllowance;
78
+ expect(validatorsMap.get('chain')?.size).toEqual(0);
79
+ });
80
+ });
81
+
82
+ describe('unregisterCallback', () => {
83
+ /**
84
+ * @target TxPot.unregisterCallback should remove registered submit validator successfully
85
+ * @dependencies
86
+ * @scenario
87
+ * - register a validator function
88
+ * - run test
89
+ * - check registered submit validators
90
+ * @expected
91
+ * - there should be no validator
92
+ */
93
+ it('should remove registered submit validator successfully', async () => {
94
+ const mockedCallback = vi.fn();
95
+ txPot.registerCallback(
96
+ 'txType',
97
+ TransactionStatus.SIGNED,
98
+ 'id',
99
+ mockedCallback
100
+ );
101
+
102
+ txPot.unregisterCallback('txType', TransactionStatus.SIGNED, 'id');
103
+
104
+ const callbacksMap: Map<
105
+ string,
106
+ Map<TransactionStatus, Map<string, CallbackFunction>>
107
+ > = (txPot as any).txTypeCallbacks;
108
+ expect(
109
+ callbacksMap.get('txType')?.get(TransactionStatus.SIGNED)?.size
110
+ ).toEqual(0);
111
+ });
112
+ });
113
+
28
114
  describe('setTransactionAsInvalid', () => {
29
115
  /**
30
116
  * @target TxPot.setTransactionAsInvalid should update status when enough blocks is passed
@@ -153,6 +239,7 @@ describe('TxPot', () => {
153
239
  txPot.registerValidator(
154
240
  testData.tx1.chain,
155
241
  testData.tx1.txType,
242
+ 'id',
156
243
  mockedValidator
157
244
  );
158
245
 
@@ -182,6 +269,7 @@ describe('TxPot', () => {
182
269
  txPot.registerValidator(
183
270
  testData.tx1.chain,
184
271
  testData.tx1.txType,
272
+ 'id',
185
273
  mockedValidator
186
274
  );
187
275
 
@@ -194,6 +282,52 @@ describe('TxPot', () => {
194
282
  expect(res).toEqual(false);
195
283
  expect(mockedSetTransactionAsInvalid).toHaveBeenCalled();
196
284
  });
285
+
286
+ /**
287
+ * @target TxPot.validateTx should return false and set tx as invalid
288
+ * when at least one validator returns false
289
+ * @dependencies
290
+ * - database
291
+ * @scenario
292
+ * - insert tx into db
293
+ * - register 3 validator functions (1st and 3rd ones returns true, 2nd one returns false)
294
+ * - mock TxPot.setTransactionAsInvalid
295
+ * - run test
296
+ * - check returned value
297
+ * - check if function got called
298
+ * @expected
299
+ * - should return false
300
+ * - `setTransactionAsInvalid` should got called
301
+ */
302
+ it('should return false and set tx as invalid when at least one validator returns false', async () => {
303
+ await txRepository.insert(testData.tx1);
304
+
305
+ const mockedValidators = [
306
+ { id: 'validator-1', validator: async (tx: TransactionEntity) => true },
307
+ {
308
+ id: 'validator-2',
309
+ validator: async (tx: TransactionEntity) => false,
310
+ },
311
+ { id: 'validator-3', validator: async (tx: TransactionEntity) => true },
312
+ ];
313
+ mockedValidators.forEach((mockedValidator) =>
314
+ txPot.registerValidator(
315
+ testData.tx1.chain,
316
+ testData.tx1.txType,
317
+ mockedValidator.id,
318
+ mockedValidator.validator
319
+ )
320
+ );
321
+
322
+ const mockedSetTransactionAsInvalid = vi.fn();
323
+ vi.spyOn(txPot as any, 'setTransactionAsInvalid').mockImplementation(
324
+ mockedSetTransactionAsInvalid
325
+ );
326
+
327
+ const res = await txPot.callValidateTx(testData.tx1);
328
+ expect(res).toEqual(false);
329
+ expect(mockedSetTransactionAsInvalid).toHaveBeenCalled();
330
+ });
197
331
  });
198
332
 
199
333
  describe('setTxStatus', () => {
@@ -262,7 +396,12 @@ describe('TxPot', () => {
262
396
  const newStatus = TransactionStatus.IN_SIGN;
263
397
  const mockedCallback = vi.fn();
264
398
  mockedCallback.mockResolvedValue(undefined);
265
- txPot.registerCallback(testData.tx1.txType, newStatus, mockedCallback);
399
+ txPot.registerCallback(
400
+ testData.tx1.txType,
401
+ newStatus,
402
+ 'id',
403
+ mockedCallback
404
+ );
266
405
 
267
406
  // run test
268
407
  await txPot.callSetTxStatus(testData.tx1, newStatus);
@@ -348,6 +487,7 @@ describe('TxPot', () => {
348
487
  * - insert tx with signed status
349
488
  * - mock PotChainManager and register to TxPot
350
489
  * - mock `submitTransaction` to throw error
490
+ * - register submit validator function
351
491
  * - run test (call `update`)
352
492
  * - check if function got called
353
493
  * - check db records
@@ -373,6 +513,13 @@ describe('TxPot', () => {
373
513
  mockedSubmitTransaction
374
514
  );
375
515
 
516
+ // register submit validator function
517
+ txPot.registerSubmitValidator(
518
+ testData.tx1.chain,
519
+ 'validator-1',
520
+ async (tx: TransactionEntity) => true
521
+ );
522
+
376
523
  // run test
377
524
  await txPot.update();
378
525
 
@@ -393,6 +540,73 @@ describe('TxPot', () => {
393
540
  ],
394
541
  ]);
395
542
  });
543
+
544
+ /**
545
+ * @target TxPot.processSignedTx should not submit the tx nor update the status
546
+ * when at least one submit validator does not allow submission
547
+ * @dependencies
548
+ * - database
549
+ * @scenario
550
+ * - insert tx with signed status
551
+ * - mock PotChainManager and register to TxPot
552
+ * - mock `submitTransaction`
553
+ * - register 3 submit validator functions
554
+ * - 1st and 3rd ones returns true
555
+ * - 2nd one returns false
556
+ * - run test (call `update`)
557
+ * - check if function got called
558
+ * - check db records
559
+ * @expected
560
+ * - `submitTransaction` should NOT got called
561
+ * - columns of the tx should remain unchanged
562
+ */
563
+ it('should not submit the tx nor update the status when at least one submit validator does not allow submission', async () => {
564
+ // insert tx with signed status
565
+ await txRepository.insert(testData.tx4);
566
+
567
+ // mock PotChainManager and register to TxPot
568
+ const mockedManager = new TestPotChainManager();
569
+ txPot.registerChain(testData.tx4.chain, mockedManager);
570
+ // mock `submitTransaction`
571
+ const mockedSubmitTransaction = vi.fn();
572
+ mockedSubmitTransaction.mockResolvedValue(undefined);
573
+ vi.spyOn(mockedManager, 'submitTransaction').mockImplementation(
574
+ mockedSubmitTransaction
575
+ );
576
+
577
+ // register 3 submit validator functions
578
+ const mockedValidators = [
579
+ { id: 'validator-1', validator: async (tx: TransactionEntity) => true },
580
+ {
581
+ id: 'validator-2',
582
+ validator: async (tx: TransactionEntity) => false,
583
+ },
584
+ { id: 'validator-3', validator: async (tx: TransactionEntity) => true },
585
+ ];
586
+ mockedValidators.forEach((mockedValidator) =>
587
+ txPot.registerSubmitValidator(
588
+ testData.tx1.chain,
589
+ mockedValidator.id,
590
+ mockedValidator.validator
591
+ )
592
+ );
593
+
594
+ // run test
595
+ await txPot.update();
596
+
597
+ // check if function got called
598
+ expect(mockedSubmitTransaction).not.toHaveBeenCalled();
599
+
600
+ // check db records
601
+ const txs = (await txRepository.find()).map((tx) => [
602
+ tx.txId,
603
+ tx.status,
604
+ tx.lastStatusUpdate,
605
+ ]);
606
+ expect(txs).toEqual([
607
+ [testData.tx4.txId, testData.tx4.status, testData.tx4.lastStatusUpdate],
608
+ ]);
609
+ });
396
610
  });
397
611
 
398
612
  describe('processesSentTx', () => {
@@ -593,6 +807,60 @@ describe('TxPot', () => {
593
807
  expect(mockedSubmitTransaction).toHaveBeenCalled();
594
808
  });
595
809
 
810
+ /**
811
+ * @target TxPot.processesSentTx should not resubmit if submit validator does not allow
812
+ * @dependencies
813
+ * - database
814
+ * @scenario
815
+ * - insert tx with sent status
816
+ * - mock PotChainManager and register to TxPot
817
+ * - mock `getTxConfirmation`
818
+ * - mock `getTxRequiredConfirmation` to return -1
819
+ * - mock `isTxInMempool` to return false
820
+ * - mock `isTxValid` to return true
821
+ * - mock `submitTransaction`
822
+ * - register a submit validator function to return false
823
+ * - run test (call `update`)
824
+ * - check if function got called
825
+ * @expected
826
+ * - `submitTransaction` should NOT got called
827
+ */
828
+ it('should not resubmit if submit validator does not allow', async () => {
829
+ // insert tx with sent status
830
+ await txRepository.insert(testData.tx6);
831
+
832
+ // mock PotChainManager and register to TxPot
833
+ const mockedManager = new TestPotChainManager();
834
+ txPot.registerChain(testData.tx6.chain, mockedManager);
835
+ // mock `getTxConfirmation`
836
+ vi.spyOn(mockedManager, 'getTxConfirmation').mockResolvedValue(-1);
837
+ // mock `getTxRequiredConfirmation`
838
+ vi.spyOn(mockedManager, 'getTxRequiredConfirmation').mockReturnValue(10);
839
+ // mock `isTxInMempool`
840
+ vi.spyOn(mockedManager, 'isTxInMempool').mockResolvedValue(false);
841
+ // mock `isTxValid`
842
+ vi.spyOn(mockedManager, 'isTxValid').mockResolvedValue(true);
843
+ // mock `submitTransaction`
844
+ const mockedSubmitTransaction = vi.fn();
845
+ mockedSubmitTransaction.mockResolvedValue(undefined);
846
+ vi.spyOn(mockedManager, 'submitTransaction').mockImplementation(
847
+ mockedSubmitTransaction
848
+ );
849
+
850
+ // register a submit validator function to return false
851
+ txPot.registerSubmitValidator(
852
+ testData.tx6.chain,
853
+ 'validator-2',
854
+ async (tx: TransactionEntity) => false
855
+ );
856
+
857
+ // run test
858
+ await txPot.update();
859
+
860
+ // check if function got called
861
+ expect(mockedSubmitTransaction).not.toHaveBeenCalled();
862
+ });
863
+
596
864
  /**
597
865
  * @target TxPot.processesSentTx should set transaction as invalid if tx is not valid anymore
598
866
  * @dependencies
@@ -669,6 +937,7 @@ describe('TxPot', () => {
669
937
  txPot.registerValidator(
670
938
  testData.tx6.chain,
671
939
  testData.tx6.txType,
940
+ 'id',
672
941
  mockedValidator
673
942
  );
674
943
 
@@ -752,11 +1021,13 @@ describe('TxPot', () => {
752
1021
  txPot.registerValidator(
753
1022
  testData.tx3.chain,
754
1023
  testData.tx3.txType,
1024
+ 'tx3-validator',
755
1025
  mockedValidator
756
1026
  );
757
1027
  txPot.registerValidator(
758
1028
  testData.tx5.chain,
759
1029
  testData.tx5.txType,
1030
+ 'tx5-validator',
760
1031
  mockedValidator
761
1032
  );
762
1033
 
@@ -807,6 +1078,42 @@ describe('TxPot', () => {
807
1078
  extra2: null,
808
1079
  });
809
1080
  });
1081
+
1082
+ /**
1083
+ * @target TxPot.addTx should insert new tx with extra fields successfully
1084
+ * @dependencies
1085
+ * - Date
1086
+ * - database
1087
+ * @scenario
1088
+ * - run test
1089
+ * - check db records
1090
+ * @expected
1091
+ * - new tx should be inserted
1092
+ */
1093
+ it('should insert new tx with extra fields successfully', async () => {
1094
+ await txPot.addTx(
1095
+ testData.tx3.txId,
1096
+ testData.tx3.chain,
1097
+ testData.tx3.txType,
1098
+ testData.tx3.requiredSign,
1099
+ testData.tx3.serializedTx,
1100
+ testData.tx3.status as TransactionStatus,
1101
+ testData.tx3.lastCheck,
1102
+ testData.tx3.extra,
1103
+ testData.tx3.extra2
1104
+ );
1105
+
1106
+ const txs = await txRepository.find();
1107
+ expect(txs.length).toEqual(1);
1108
+ expect(txs[0]).toEqual({
1109
+ ...testData.tx3,
1110
+ failedInSign: false,
1111
+ signFailedCount: 0,
1112
+ lastStatusUpdate: String(testData.currentTimeStampAsSeconds),
1113
+ extra: testData.tx3.extra,
1114
+ extra2: null,
1115
+ });
1116
+ });
810
1117
  });
811
1118
 
812
1119
  describe('setTxStatusById', () => {
@@ -1514,4 +1821,61 @@ describe('TxPot', () => {
1514
1821
  expect(txs[1].txId).toEqual(testData.tx2.txId);
1515
1822
  });
1516
1823
  });
1824
+
1825
+ describe('updateExtra', () => {
1826
+ /**
1827
+ * @target TxPot.updateExtra should update extra columns successfully
1828
+ * @dependencies
1829
+ * - database
1830
+ * @scenario
1831
+ * - insert 2 txs
1832
+ * - run test
1833
+ * - check db records
1834
+ * @expected
1835
+ * - extra of target tx should be updated
1836
+ * - extra of other txs should remain unchanged
1837
+ */
1838
+ it('should update extra columns successfully', async () => {
1839
+ await txRepository.insert(testData.tx3);
1840
+ await txRepository.insert(testData.tx4);
1841
+
1842
+ const updatedExtra = 'new-extra';
1843
+ await txPot.updateExtra(
1844
+ testData.tx3.txId,
1845
+ testData.tx3.chain,
1846
+ updatedExtra
1847
+ );
1848
+
1849
+ const txs = (await txRepository.find()).map((tx) => [tx.txId, tx.extra]);
1850
+ expect(txs).toEqual([
1851
+ [testData.tx3.txId, updatedExtra],
1852
+ [testData.tx4.txId, testData.tx4.extra],
1853
+ ]);
1854
+ });
1855
+
1856
+ /**
1857
+ * @target TxPot.updateExtra should set null successfully
1858
+ * @dependencies
1859
+ * - database
1860
+ * @scenario
1861
+ * - insert a tx with extra
1862
+ * - run test
1863
+ * - check db records
1864
+ * @expected
1865
+ * - extra of target tx should be updated
1866
+ */
1867
+ it('should set null successfully', async () => {
1868
+ await txRepository.insert(testData.tx3);
1869
+
1870
+ const updatedExtra = null;
1871
+ await txPot.updateExtra(
1872
+ testData.tx3.txId,
1873
+ testData.tx3.chain,
1874
+ updatedExtra
1875
+ );
1876
+
1877
+ const txs = (await txRepository.find()).map((tx) => [tx.txId, tx.extra]);
1878
+ expect(txs).toEqual([[testData.tx3.txId, updatedExtra]]);
1879
+ });
1880
+ });
1517
1881
  });