@rosen-bridge/abstract-extractor 2.1.0 → 2.1.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.
- package/CHANGELOG.md +11 -0
- package/package.json +14 -7
- package/.eslintignore +0 -1
- package/dist/lib/abstractExtractor.d.ts +0 -25
- package/dist/lib/abstractExtractor.d.ts.map +0 -1
- package/dist/lib/abstractExtractor.js +0 -3
- package/dist/lib/constants.d.ts +0 -4
- package/dist/lib/constants.d.ts.map +0 -1
- package/dist/lib/constants.js +0 -4
- package/dist/lib/ergo/AbstractErgoExtractor.d.ts +0 -80
- package/dist/lib/ergo/AbstractErgoExtractor.d.ts.map +0 -1
- package/dist/lib/ergo/AbstractErgoExtractor.js +0 -142
- package/dist/lib/ergo/AbstractErgoExtractorAction.d.ts +0 -92
- package/dist/lib/ergo/AbstractErgoExtractorAction.d.ts.map +0 -1
- package/dist/lib/ergo/AbstractErgoExtractorAction.js +0 -222
- package/dist/lib/ergo/AbstractErgoExtractorEntity.d.ts +0 -11
- package/dist/lib/ergo/AbstractErgoExtractorEntity.d.ts.map +0 -1
- package/dist/lib/ergo/AbstractErgoExtractorEntity.js +0 -57
- package/dist/lib/ergo/index.d.ts +0 -10
- package/dist/lib/ergo/index.d.ts.map +0 -1
- package/dist/lib/ergo/index.js +0 -10
- package/dist/lib/ergo/initializable/AbstractInitializable.d.ts +0 -48
- package/dist/lib/ergo/initializable/AbstractInitializable.d.ts.map +0 -1
- package/dist/lib/ergo/initializable/AbstractInitializable.js +0 -163
- package/dist/lib/ergo/initializable/AbstractInitializableAction.d.ts +0 -14
- package/dist/lib/ergo/initializable/AbstractInitializableAction.d.ts.map +0 -1
- package/dist/lib/ergo/initializable/AbstractInitializableAction.js +0 -16
- package/dist/lib/ergo/initializable/index.d.ts +0 -3
- package/dist/lib/ergo/initializable/index.d.ts.map +0 -1
- package/dist/lib/ergo/initializable/index.js +0 -3
- package/dist/lib/ergo/interfaces.d.ts +0 -47
- package/dist/lib/ergo/interfaces.d.ts.map +0 -1
- package/dist/lib/ergo/interfaces.js +0 -8
- package/dist/lib/ergo/network/AbstractNetwork.d.ts +0 -26
- package/dist/lib/ergo/network/AbstractNetwork.d.ts.map +0 -1
- package/dist/lib/ergo/network/AbstractNetwork.js +0 -3
- package/dist/lib/ergo/network/ExplorerNetwork.d.ts +0 -74
- package/dist/lib/ergo/network/ExplorerNetwork.d.ts.map +0 -1
- package/dist/lib/ergo/network/ExplorerNetwork.js +0 -185
- package/dist/lib/ergo/network/NodeNetwork.d.ts +0 -60
- package/dist/lib/ergo/network/NodeNetwork.d.ts.map +0 -1
- package/dist/lib/ergo/network/NodeNetwork.js +0 -131
- package/dist/lib/ergo/utils.d.ts +0 -8
- package/dist/lib/ergo/utils.d.ts.map +0 -1
- package/dist/lib/ergo/utils.js +0 -16
- package/dist/lib/index.d.ts +0 -4
- package/dist/lib/index.d.ts.map +0 -1
- package/dist/lib/index.js +0 -4
- package/dist/tests/AbstractErgoExtractor.mock.d.ts +0 -11
- package/dist/tests/AbstractErgoExtractor.mock.d.ts.map +0 -1
- package/dist/tests/AbstractErgoExtractor.mock.js +0 -11
- package/dist/tests/AbstractErgoExtractor.spec.d.ts +0 -2
- package/dist/tests/AbstractErgoExtractor.spec.d.ts.map +0 -1
- package/dist/tests/AbstractErgoExtractor.spec.js +0 -240
- package/dist/tests/AbstractErgoExtractorAction.mock.d.ts +0 -15
- package/dist/tests/AbstractErgoExtractorAction.mock.d.ts.map +0 -1
- package/dist/tests/AbstractErgoExtractorAction.mock.js +0 -27
- package/dist/tests/AbstractErgoExtractorAction.spec.d.ts +0 -2
- package/dist/tests/AbstractErgoExtractorAction.spec.d.ts.map +0 -1
- package/dist/tests/AbstractErgoExtractorAction.spec.js +0 -221
- package/dist/tests/initializable/AbstractInitializable.mock.d.ts +0 -61
- package/dist/tests/initializable/AbstractInitializable.mock.d.ts.map +0 -1
- package/dist/tests/initializable/AbstractInitializable.mock.js +0 -18
- package/dist/tests/initializable/AbstractInitializable.spec.d.ts +0 -2
- package/dist/tests/initializable/AbstractInitializable.spec.d.ts.map +0 -1
- package/dist/tests/initializable/AbstractInitializable.spec.js +0 -299
- package/dist/tests/initializable/AbstractInitializableAction.mock.d.ts +0 -15
- package/dist/tests/initializable/AbstractInitializableAction.mock.d.ts.map +0 -1
- package/dist/tests/initializable/AbstractInitializableAction.mock.js +0 -27
- package/dist/tests/initializable/AbstractInitializableAction.spec.d.ts +0 -2
- package/dist/tests/initializable/AbstractInitializableAction.spec.d.ts.map +0 -1
- package/dist/tests/initializable/AbstractInitializableAction.spec.js +0 -54
- package/dist/tests/initializable/testData.d.ts +0 -94
- package/dist/tests/initializable/testData.d.ts.map +0 -1
- package/dist/tests/initializable/testData.js +0 -235
- package/dist/tests/network/ExplorerNetwork.spec.d.ts +0 -2
- package/dist/tests/network/ExplorerNetwork.spec.d.ts.map +0 -1
- package/dist/tests/network/ExplorerNetwork.spec.js +0 -61
- package/dist/tests/network/NodeNetwork.spec.d.ts +0 -2
- package/dist/tests/network/NodeNetwork.spec.d.ts.map +0 -1
- package/dist/tests/network/NodeNetwork.spec.js +0 -48
- package/dist/tests/network/testData.d.ts +0 -752
- package/dist/tests/network/testData.d.ts.map +0 -1
- package/dist/tests/network/testData.js +0 -1401
- package/dist/tests/testData.d.ts +0 -32
- package/dist/tests/testData.d.ts.map +0 -1
- package/dist/tests/testData.js +0 -121
- package/dist/tests/testUtils.d.ts +0 -9
- package/dist/tests/testUtils.d.ts.map +0 -1
- package/dist/tests/testUtils.js +0 -31
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/dist/vitest.config.d.ts +0 -3
- package/dist/vitest.config.d.ts.map +0 -1
- package/dist/vitest.config.js +0 -18
- package/lib/abstractExtractor.ts +0 -31
- package/lib/constants.ts +0 -3
- package/lib/ergo/AbstractErgoExtractor.ts +0 -227
- package/lib/ergo/AbstractErgoExtractorAction.ts +0 -319
- package/lib/ergo/AbstractErgoExtractorEntity.ts +0 -32
- package/lib/ergo/index.ts +0 -9
- package/lib/ergo/initializable/AbstractInitializable.ts +0 -228
- package/lib/ergo/initializable/AbstractInitializableAction.ts +0 -33
- package/lib/ergo/initializable/index.ts +0 -2
- package/lib/ergo/interfaces.ts +0 -51
- package/lib/ergo/network/AbstractNetwork.ts +0 -29
- package/lib/ergo/network/ExplorerNetwork.ts +0 -237
- package/lib/ergo/network/NodeNetwork.ts +0 -169
- package/lib/ergo/utils.ts +0 -15
- package/lib/index.ts +0 -3
- package/tests/AbstractErgoExtractor.mock.ts +0 -33
- package/tests/AbstractErgoExtractor.spec.ts +0 -284
- package/tests/AbstractErgoExtractorAction.mock.ts +0 -45
- package/tests/AbstractErgoExtractorAction.spec.ts +0 -268
- package/tests/initializable/AbstractInitializable.mock.ts +0 -44
- package/tests/initializable/AbstractInitializable.spec.ts +0 -386
- package/tests/initializable/AbstractInitializableAction.mock.ts +0 -45
- package/tests/initializable/AbstractInitializableAction.spec.ts +0 -64
- package/tests/initializable/testData.ts +0 -283
- package/tests/network/ExplorerNetwork.spec.ts +0 -73
- package/tests/network/NodeNetwork.spec.ts +0 -56
- package/tests/network/testData.ts +0 -1708
- package/tests/testData.ts +0 -140
- package/tests/testUtils.ts +0 -22
- package/tsconfig.build.json +0 -9
- package/tsconfig.build.tsbuildinfo +0 -1
- package/tsconfig.json +0 -9
- package/vitest.config.ts +0 -18
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
import ergoNodeClientFactory, {
|
|
2
|
-
IndexedErgoBox,
|
|
3
|
-
IndexedErgoTransaction,
|
|
4
|
-
} from '@rosen-clients/ergo-node';
|
|
5
|
-
import { BlockInfo } from '@rosen-bridge/scanner-interfaces';
|
|
6
|
-
|
|
7
|
-
import { ErgoBox, ExtendedTransaction } from '../interfaces';
|
|
8
|
-
import { AbstractNetwork } from './AbstractNetwork';
|
|
9
|
-
|
|
10
|
-
export class NodeNetwork extends AbstractNetwork {
|
|
11
|
-
private api;
|
|
12
|
-
|
|
13
|
-
constructor(url: string) {
|
|
14
|
-
super();
|
|
15
|
-
this.api = ergoNodeClientFactory(url);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* convert node api boxes to ErgoBox interface
|
|
20
|
-
* @param box
|
|
21
|
-
* @returns ErgoBox
|
|
22
|
-
*/
|
|
23
|
-
private convertBox = async (box: IndexedErgoBox): Promise<ErgoBox> => {
|
|
24
|
-
const tx = await await this.api.getTxById(box.transactionId!);
|
|
25
|
-
const spendInfo = box.spentTransactionId
|
|
26
|
-
? await this.getSpendingInfo(box.boxId!, box.spentTransactionId)
|
|
27
|
-
: undefined;
|
|
28
|
-
return {
|
|
29
|
-
transactionId: box.transactionId || '',
|
|
30
|
-
index: box.index || 0,
|
|
31
|
-
value: box.value || 0n,
|
|
32
|
-
ergoTree: box.ergoTree || '',
|
|
33
|
-
creationHeight: box.creationHeight || 0,
|
|
34
|
-
inclusionHeight: tx.inclusionHeight,
|
|
35
|
-
assets: box.assets || [],
|
|
36
|
-
additionalRegisters: box.additionalRegisters,
|
|
37
|
-
boxId: box.boxId || '',
|
|
38
|
-
blockId: tx.blockId,
|
|
39
|
-
spentBlockId: spendInfo?.hash,
|
|
40
|
-
spentHeight: spendInfo?.height,
|
|
41
|
-
spentTransactionId: box.spentTransactionId,
|
|
42
|
-
spentIndex: spendInfo?.spendIndex,
|
|
43
|
-
};
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* convert Node transaction to extractor transaction type
|
|
48
|
-
* @param tx
|
|
49
|
-
*/
|
|
50
|
-
private convertTransaction = (
|
|
51
|
-
tx: IndexedErgoTransaction,
|
|
52
|
-
): ExtendedTransaction => {
|
|
53
|
-
return {
|
|
54
|
-
id: tx.id || '',
|
|
55
|
-
inclusionHeight: tx.inclusionHeight,
|
|
56
|
-
blockId: tx.blockId,
|
|
57
|
-
outputs: tx.outputs.map((output) => ({
|
|
58
|
-
transactionId: output.transactionId || '',
|
|
59
|
-
index: output.index || 0,
|
|
60
|
-
value: output.value || 0n,
|
|
61
|
-
ergoTree: output.ergoTree || '',
|
|
62
|
-
creationHeight: output.creationHeight || 0,
|
|
63
|
-
assets: output.assets || [],
|
|
64
|
-
additionalRegisters: output.additionalRegisters,
|
|
65
|
-
boxId: output.boxId || '',
|
|
66
|
-
})),
|
|
67
|
-
// TODO: Add input extension local/ergo/rosen-bridge/scanner/-/issues/156
|
|
68
|
-
inputs: tx.inputs.map((input) => ({ boxId: input.boxId! })) ?? [],
|
|
69
|
-
dataInputs: tx.dataInputs,
|
|
70
|
-
};
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* return spending information of a specified box by having spendTxId
|
|
75
|
-
* @param boxId
|
|
76
|
-
* @param spendTxId
|
|
77
|
-
*/
|
|
78
|
-
getSpendingInfo = async (
|
|
79
|
-
boxId: string,
|
|
80
|
-
spendTxId: string,
|
|
81
|
-
): Promise<BlockInfo & { spendIndex: number }> => {
|
|
82
|
-
const tx = await this.api.getTxById(spendTxId);
|
|
83
|
-
const spendIndex = tx.inputs?.findIndex((box) => box.boxId == boxId);
|
|
84
|
-
if (spendIndex == undefined)
|
|
85
|
-
throw Error(
|
|
86
|
-
`Impossible behavior, the box [${boxId}] should have been spent in tx [${spendTxId}]`,
|
|
87
|
-
);
|
|
88
|
-
return {
|
|
89
|
-
hash: tx.blockId,
|
|
90
|
-
height: tx.inclusionHeight,
|
|
91
|
-
spendIndex,
|
|
92
|
-
};
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* use node api to return related boxes by specified address
|
|
97
|
-
* @param address
|
|
98
|
-
* @param offset
|
|
99
|
-
* @param limit
|
|
100
|
-
* @returns related boxes
|
|
101
|
-
*/
|
|
102
|
-
getBoxesByAddress = async (
|
|
103
|
-
address: string,
|
|
104
|
-
offset: number,
|
|
105
|
-
limit: number,
|
|
106
|
-
): Promise<{ boxes: ErgoBox[]; hasNextBatch: boolean }> => {
|
|
107
|
-
const boxes = await this.api.getBoxesByAddressUnspent(address, {
|
|
108
|
-
offset: offset,
|
|
109
|
-
limit: limit,
|
|
110
|
-
sortDirection: 'desc',
|
|
111
|
-
});
|
|
112
|
-
if (!boxes)
|
|
113
|
-
throw new Error('Ergo node BoxesByAddress api expected to have items');
|
|
114
|
-
const ergoBoxes = await Promise.all(
|
|
115
|
-
boxes.map(async (box) => await this.convertBox(box)),
|
|
116
|
-
);
|
|
117
|
-
return { boxes: ergoBoxes, hasNextBatch: boxes.length > 0 };
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* use node api to return related boxes by specified token id
|
|
122
|
-
* @param tokenId
|
|
123
|
-
* @param offset
|
|
124
|
-
* @param limit
|
|
125
|
-
* @returns related boxes
|
|
126
|
-
*/
|
|
127
|
-
getBoxesByTokenId = async (
|
|
128
|
-
tokenId: string,
|
|
129
|
-
offset: number,
|
|
130
|
-
limit: number,
|
|
131
|
-
): Promise<{ boxes: ErgoBox[]; hasNextBatch: boolean }> => {
|
|
132
|
-
const boxes = await this.api.getBoxesByTokenId(tokenId, {
|
|
133
|
-
offset: offset,
|
|
134
|
-
limit: limit,
|
|
135
|
-
});
|
|
136
|
-
if (!boxes.items)
|
|
137
|
-
throw new Error('Ergo node BoxesByTokenId api expected to have items');
|
|
138
|
-
const ergoBoxes = await Promise.all(
|
|
139
|
-
boxes.items.map(async (box) => await this.convertBox(box)),
|
|
140
|
-
);
|
|
141
|
-
return { boxes: ergoBoxes, hasNextBatch: boxes.items.length > 0 };
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* use node api to return related transactions of the specified address with limit offset
|
|
146
|
-
* @param tokenId
|
|
147
|
-
* @param offset
|
|
148
|
-
* @param limit
|
|
149
|
-
* @returns related transactions
|
|
150
|
-
*/
|
|
151
|
-
getAddressTransactionsWithOffsetLimit = async (
|
|
152
|
-
address: string,
|
|
153
|
-
offset: number,
|
|
154
|
-
limit: number,
|
|
155
|
-
): Promise<{ items: Array<ExtendedTransaction>; total: number }> => {
|
|
156
|
-
const txs = await this.api.getTxsByAddress(address, {
|
|
157
|
-
offset,
|
|
158
|
-
limit,
|
|
159
|
-
});
|
|
160
|
-
if (!txs.items)
|
|
161
|
-
throw new Error(
|
|
162
|
-
'Explorer AddressTransactions api expected to have items',
|
|
163
|
-
);
|
|
164
|
-
return {
|
|
165
|
-
items: txs.items.map((tx) => this.convertTransaction(tx)),
|
|
166
|
-
total: txs.total!,
|
|
167
|
-
};
|
|
168
|
-
};
|
|
169
|
-
}
|
package/lib/ergo/utils.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { intersection } from 'lodash-es';
|
|
2
|
-
import { OutputBox } from '@rosen-bridge/scanner-interfaces';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Check box to have specified tokens
|
|
6
|
-
* @param box
|
|
7
|
-
* @return true if box has the required token and false otherwise
|
|
8
|
-
*/
|
|
9
|
-
export const boxHasToken = (box: OutputBox, tokenIds: string[]) => {
|
|
10
|
-
if (!box.assets) return false;
|
|
11
|
-
const boxTokens = box.assets.map((token) => token.tokenId);
|
|
12
|
-
const requiredTokens = intersection(tokenIds, boxTokens);
|
|
13
|
-
if (requiredTokens.length == tokenIds.length) return true;
|
|
14
|
-
return false;
|
|
15
|
-
};
|
package/lib/index.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { V1 } from '@rosen-clients/ergo-explorer';
|
|
2
|
-
import { BlockInfo, OutputBox } from '@rosen-bridge/scanner-interfaces';
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
AbstractErgoExtractor,
|
|
6
|
-
AbstractBoxData,
|
|
7
|
-
AbstractErgoExtractorAction,
|
|
8
|
-
AbstractErgoExtractorEntity,
|
|
9
|
-
} from '../lib';
|
|
10
|
-
|
|
11
|
-
export class MockedErgoExtractor extends AbstractErgoExtractor<
|
|
12
|
-
AbstractBoxData,
|
|
13
|
-
AbstractErgoExtractorEntity
|
|
14
|
-
> {
|
|
15
|
-
actions: AbstractErgoExtractorAction<
|
|
16
|
-
AbstractBoxData,
|
|
17
|
-
AbstractErgoExtractorEntity
|
|
18
|
-
>;
|
|
19
|
-
|
|
20
|
-
getId = () => 'Test';
|
|
21
|
-
|
|
22
|
-
initializeBoxes: (initialBlock: BlockInfo) => Promise<void>;
|
|
23
|
-
|
|
24
|
-
hasData = (
|
|
25
|
-
box: V1.OutputInfo | OutputBox, // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
26
|
-
) => false;
|
|
27
|
-
|
|
28
|
-
extractBoxData = (
|
|
29
|
-
box: V1.OutputInfo | OutputBox, // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
30
|
-
): AbstractBoxData | undefined => {
|
|
31
|
-
return undefined;
|
|
32
|
-
};
|
|
33
|
-
}
|
|
@@ -1,284 +0,0 @@
|
|
|
1
|
-
import { V1 } from '@rosen-clients/ergo-explorer';
|
|
2
|
-
import { OutputBox } from '@rosen-bridge/scanner-interfaces';
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
AbstractBoxData,
|
|
6
|
-
AbstractErgoExtractorAction,
|
|
7
|
-
CallbackType,
|
|
8
|
-
AbstractErgoExtractorEntity,
|
|
9
|
-
} from '../lib';
|
|
10
|
-
import { block, extractedData, tx } from './testData';
|
|
11
|
-
import { MockedErgoExtractor } from './AbstractErgoExtractor.mock';
|
|
12
|
-
|
|
13
|
-
describe('AbstractErgoExtractor', () => {
|
|
14
|
-
describe('processTransactions', () => {
|
|
15
|
-
/**
|
|
16
|
-
* @target processTransactions should initialize extractor with specified id and insert status into db
|
|
17
|
-
* @dependencies
|
|
18
|
-
* @scenario
|
|
19
|
-
* - mock extractor
|
|
20
|
-
* - mock `hasData` to return true for one box
|
|
21
|
-
* - spy `extractBoxData` and `storeBoxes`
|
|
22
|
-
* - run test (call `processTransactions`)
|
|
23
|
-
* @expected
|
|
24
|
-
* - to call `extractBoxData` for the specific box and all input extensions
|
|
25
|
-
* - to insert the extracted box to database
|
|
26
|
-
* - to return true when total procedure is successful
|
|
27
|
-
* - to trigger `INSERT` callbacks with correct data
|
|
28
|
-
*/
|
|
29
|
-
it('should process boxes with data and insert data into database', async () => {
|
|
30
|
-
const extractor = new MockedErgoExtractor();
|
|
31
|
-
const triggerCallbacks = vitest.fn();
|
|
32
|
-
extractor['triggerCallbacks'] = triggerCallbacks;
|
|
33
|
-
extractor.hasData = (box: V1.OutputInfo | OutputBox) => {
|
|
34
|
-
if (box.boxId == tx.outputs[0].boxId) return true;
|
|
35
|
-
return false;
|
|
36
|
-
};
|
|
37
|
-
const extractSpy = vitest.fn().mockReturnValue(extractedData);
|
|
38
|
-
extractor.extractBoxData = extractSpy;
|
|
39
|
-
const storeSpy = vitest.fn().mockResolvedValue(true);
|
|
40
|
-
const spendSpy = vitest.fn().mockResolvedValue([]);
|
|
41
|
-
extractor['actions'] = {
|
|
42
|
-
storeBoxes: storeSpy,
|
|
43
|
-
spendBoxes: spendSpy,
|
|
44
|
-
} as unknown as AbstractErgoExtractorAction<
|
|
45
|
-
AbstractBoxData,
|
|
46
|
-
AbstractErgoExtractorEntity
|
|
47
|
-
>;
|
|
48
|
-
const result = await extractor.processTransactions([tx], block);
|
|
49
|
-
|
|
50
|
-
expect(extractSpy).toBeCalledTimes(1);
|
|
51
|
-
expect(extractSpy).toBeCalledWith(
|
|
52
|
-
tx.outputs[0],
|
|
53
|
-
[tx.inputs[0].extension, {}],
|
|
54
|
-
{},
|
|
55
|
-
);
|
|
56
|
-
expect(storeSpy).toBeCalledWith([extractedData], block, 'Test');
|
|
57
|
-
expect(result).toEqual(true);
|
|
58
|
-
expect(triggerCallbacks).toBeCalledWith(CallbackType.Insert, [
|
|
59
|
-
extractedData,
|
|
60
|
-
]);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* @target processTransactions should extract spending information of all input boxes
|
|
65
|
-
* @dependencies
|
|
66
|
-
* @scenario
|
|
67
|
-
* - mock extractor (hasData returns false as default)
|
|
68
|
-
* - spy `extractBoxData`, `storeBoxes` and `spendBoxes`
|
|
69
|
-
* - run test (call `processTransactions`)
|
|
70
|
-
* @expected
|
|
71
|
-
* - not to call `extractBoxData` and `storeBoxes` when there is not any box with data
|
|
72
|
-
* - to extractor spend info of input boxes and call `spendBoxes`
|
|
73
|
-
* - to return true when total procedure is successful
|
|
74
|
-
* - to trigger `SPEND` callbacks with correct data
|
|
75
|
-
*/
|
|
76
|
-
it('should extract spending information of all input boxes', async () => {
|
|
77
|
-
const extractor = new MockedErgoExtractor();
|
|
78
|
-
const triggerCallbacks = vitest.fn();
|
|
79
|
-
extractor['triggerCallbacks'] = triggerCallbacks;
|
|
80
|
-
const extractSpy = vitest.fn();
|
|
81
|
-
extractor.extractBoxData = extractSpy;
|
|
82
|
-
const storeSpy = vitest.fn().mockResolvedValue(true);
|
|
83
|
-
const spendSpy = vitest
|
|
84
|
-
.fn()
|
|
85
|
-
.mockResolvedValue([
|
|
86
|
-
{ boxId: tx.inputs[0].boxId },
|
|
87
|
-
{ boxId: tx.inputs[1].boxId },
|
|
88
|
-
]);
|
|
89
|
-
extractor['actions'] = {
|
|
90
|
-
storeBoxes: storeSpy,
|
|
91
|
-
spendBoxes: spendSpy,
|
|
92
|
-
} as unknown as AbstractErgoExtractorAction<
|
|
93
|
-
AbstractBoxData,
|
|
94
|
-
AbstractErgoExtractorEntity
|
|
95
|
-
>;
|
|
96
|
-
const result = await extractor.processTransactions([tx], block);
|
|
97
|
-
|
|
98
|
-
expect(extractSpy).not.toBeCalled();
|
|
99
|
-
expect(storeSpy).not.toBeCalled();
|
|
100
|
-
expect(spendSpy).toBeCalledWith(
|
|
101
|
-
[
|
|
102
|
-
{ boxId: tx.inputs[0].boxId, txId: tx.id, index: 1 },
|
|
103
|
-
{ boxId: tx.inputs[1].boxId, txId: tx.id, index: 2 },
|
|
104
|
-
],
|
|
105
|
-
block,
|
|
106
|
-
'Test',
|
|
107
|
-
);
|
|
108
|
-
expect(result).toEqual(true);
|
|
109
|
-
expect(triggerCallbacks).toBeCalledWith(CallbackType.Spend, [
|
|
110
|
-
{ boxId: tx.inputs[0].boxId },
|
|
111
|
-
{ boxId: tx.inputs[1].boxId },
|
|
112
|
-
]);
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* @target processTransactions should return false if data insertion fails
|
|
117
|
-
* @dependencies
|
|
118
|
-
* @scenario
|
|
119
|
-
* - mock extractor
|
|
120
|
-
* - mock `hasData` to return true for one box
|
|
121
|
-
* - spy `extractBoxData` and `storeBoxes`
|
|
122
|
-
* - run test (call `processTransactions`)
|
|
123
|
-
* @expected
|
|
124
|
-
* - to return false when `insertBoxes` returns false
|
|
125
|
-
* - not to call `spendBoxes` if data insertion fails
|
|
126
|
-
*/
|
|
127
|
-
it('should return false if data insertion fails', async () => {
|
|
128
|
-
const extractor = new MockedErgoExtractor();
|
|
129
|
-
extractor.hasData = (box: V1.OutputInfo | OutputBox) => {
|
|
130
|
-
if (box.boxId == tx.outputs[0].boxId) return true;
|
|
131
|
-
return false;
|
|
132
|
-
};
|
|
133
|
-
const extractSpy = vitest.fn().mockReturnValue(extractedData);
|
|
134
|
-
extractor.extractBoxData = extractSpy;
|
|
135
|
-
const storeSpy = vitest.fn().mockResolvedValue(false);
|
|
136
|
-
const spendSpy = vitest.fn();
|
|
137
|
-
extractor['actions'] = {
|
|
138
|
-
storeBoxes: storeSpy,
|
|
139
|
-
spendBoxes: spendSpy,
|
|
140
|
-
} as unknown as AbstractErgoExtractorAction<
|
|
141
|
-
AbstractBoxData,
|
|
142
|
-
AbstractErgoExtractorEntity
|
|
143
|
-
>;
|
|
144
|
-
const result = await extractor.processTransactions([tx], block);
|
|
145
|
-
|
|
146
|
-
expect(result).toEqual(false);
|
|
147
|
-
expect(spendSpy).not.toBeCalled();
|
|
148
|
-
});
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
describe('forkBlock', () => {
|
|
152
|
-
/**
|
|
153
|
-
* @target forkBlock should remove all data extracted from the specified block
|
|
154
|
-
* @dependencies
|
|
155
|
-
* @scenario
|
|
156
|
-
* - mock extractor
|
|
157
|
-
* - spy `deleteBlockBoxes`
|
|
158
|
-
* - run test (call `forkBlock`)
|
|
159
|
-
* @expected
|
|
160
|
-
* - to call `deleteBlockBoxes` for the specific box
|
|
161
|
-
* - to trigger `DELETE` callbacks for the deleted box
|
|
162
|
-
* - to trigger `UPDATE` callbacks for the spent box
|
|
163
|
-
*/
|
|
164
|
-
it('should remove all data extracted from the specified block', async () => {
|
|
165
|
-
const extractor = new MockedErgoExtractor();
|
|
166
|
-
const removeSpy = vitest.fn().mockResolvedValue({
|
|
167
|
-
deletedData: [{ boxId: 'box1' }],
|
|
168
|
-
updatedData: [{ boxId: 'box2' }],
|
|
169
|
-
});
|
|
170
|
-
extractor['actions'] = {
|
|
171
|
-
deleteBlockBoxes: removeSpy,
|
|
172
|
-
} as unknown as AbstractErgoExtractorAction<
|
|
173
|
-
AbstractBoxData,
|
|
174
|
-
AbstractErgoExtractorEntity
|
|
175
|
-
>;
|
|
176
|
-
const triggerCallbackSpy = vitest.fn().mockClear();
|
|
177
|
-
extractor['triggerCallbacks'] = triggerCallbackSpy;
|
|
178
|
-
await extractor.forkBlock(block.hash);
|
|
179
|
-
expect(removeSpy).toBeCalledWith(block.hash, 'Test');
|
|
180
|
-
expect(triggerCallbackSpy).toBeCalledWith(CallbackType.Delete, [
|
|
181
|
-
{ boxId: 'box1' },
|
|
182
|
-
]);
|
|
183
|
-
expect(triggerCallbackSpy).toBeCalledWith(CallbackType.Update, [
|
|
184
|
-
{ boxId: 'box2' },
|
|
185
|
-
]);
|
|
186
|
-
});
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
describe('hook', () => {
|
|
190
|
-
/**
|
|
191
|
-
* @target hook should hook a new callback on insert with the new id
|
|
192
|
-
* @dependencies
|
|
193
|
-
* @scenario
|
|
194
|
-
* - mock extractor
|
|
195
|
-
* - mock a callback for insert
|
|
196
|
-
* - run test (call `hook`)
|
|
197
|
-
* @expected
|
|
198
|
-
* - hook the callback with the specified id
|
|
199
|
-
* - return true
|
|
200
|
-
*/
|
|
201
|
-
it('should hook a new callback on insert with the new id', async () => {
|
|
202
|
-
const extractor = new MockedErgoExtractor();
|
|
203
|
-
const insertCallback = vitest.fn();
|
|
204
|
-
const id = await extractor.hook(CallbackType.Insert, insertCallback);
|
|
205
|
-
expect(extractor['callbacks'][CallbackType.Insert]).toEqual(
|
|
206
|
-
new Map().set(id, insertCallback),
|
|
207
|
-
);
|
|
208
|
-
});
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
describe('unhook', () => {
|
|
212
|
-
/**
|
|
213
|
-
* @target unhook should unhook the callback on insert with the specified id
|
|
214
|
-
* @dependencies
|
|
215
|
-
* @scenario
|
|
216
|
-
* - mock extractor
|
|
217
|
-
* - mock a callback for insert
|
|
218
|
-
* - hook the callback
|
|
219
|
-
* - run test (call `unhook`)
|
|
220
|
-
* @expected
|
|
221
|
-
* - unhook the callback with the specified id
|
|
222
|
-
* - return true
|
|
223
|
-
*/
|
|
224
|
-
it('should unhook the callback on insert with the specified id', async () => {
|
|
225
|
-
const extractor = new MockedErgoExtractor();
|
|
226
|
-
const insertCallback = vitest.fn();
|
|
227
|
-
const id = await extractor.hook(CallbackType.Insert, insertCallback);
|
|
228
|
-
const result = await extractor.unhook(CallbackType.Insert, id);
|
|
229
|
-
expect(result).toBeTruthy();
|
|
230
|
-
expect(extractor['callbacks'][CallbackType.Insert].get(id)).toEqual(
|
|
231
|
-
undefined,
|
|
232
|
-
);
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* @target unhook should not unhook callbacks with the same id on other types
|
|
237
|
-
* @dependencies
|
|
238
|
-
* @scenario
|
|
239
|
-
* - mock extractor
|
|
240
|
-
* - mock two callbacks for insert
|
|
241
|
-
* - hook the first callback
|
|
242
|
-
* - run test (call `unhook` with the same id for second callback)
|
|
243
|
-
* @expected
|
|
244
|
-
* - not to change hooked callbacks when the callback with the id
|
|
245
|
-
* doesn't exists on the specified type
|
|
246
|
-
* - return false
|
|
247
|
-
*/
|
|
248
|
-
it('should not unhook callbacks with the same id on other types', async () => {
|
|
249
|
-
const extractor = new MockedErgoExtractor();
|
|
250
|
-
const insertCallback = vitest.fn();
|
|
251
|
-
const id = await extractor.hook(CallbackType.Insert, insertCallback);
|
|
252
|
-
const result = await extractor.unhook(CallbackType.Update, id);
|
|
253
|
-
expect(result).toBeFalsy();
|
|
254
|
-
expect(extractor['callbacks'][CallbackType.Insert].get(id)).toEqual(
|
|
255
|
-
insertCallback,
|
|
256
|
-
);
|
|
257
|
-
expect(extractor['callbacks'][CallbackType.Update].get(id)).toEqual(
|
|
258
|
-
undefined,
|
|
259
|
-
);
|
|
260
|
-
});
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
describe('triggerCallbacks', () => {
|
|
264
|
-
/**
|
|
265
|
-
* @target triggerCallbacks should trigger all callbacks hooked on a type
|
|
266
|
-
* @dependencies
|
|
267
|
-
* @scenario
|
|
268
|
-
* - mock extractor
|
|
269
|
-
* - mock a callback for insert
|
|
270
|
-
* - hook the callback
|
|
271
|
-
* - run test (call `triggerCallbacks` for insert type)
|
|
272
|
-
* @expected
|
|
273
|
-
* - trigger all callbacks with the specified id
|
|
274
|
-
*/
|
|
275
|
-
it('should trigger all callbacks hooked on a type', async () => {
|
|
276
|
-
const extractor = new MockedErgoExtractor();
|
|
277
|
-
const insertCallback = vitest.fn();
|
|
278
|
-
await extractor.hook(CallbackType.Insert, insertCallback);
|
|
279
|
-
const insertedData = [{ boxId: 'boxId', serialized: 'serialized' }];
|
|
280
|
-
await extractor['triggerCallbacks'](CallbackType.Insert, insertedData);
|
|
281
|
-
expect(insertCallback).toBeCalledWith(insertedData);
|
|
282
|
-
});
|
|
283
|
-
});
|
|
284
|
-
});
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { DataSource } from '@rosen-bridge/extended-typeorm';
|
|
2
|
-
import { pick } from 'lodash-es';
|
|
3
|
-
import { BlockInfo } from '@rosen-bridge/scanner-interfaces';
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
AbstractErgoExtractorAction,
|
|
7
|
-
AbstractErgoExtractorEntity,
|
|
8
|
-
AbstractBoxData,
|
|
9
|
-
} from '../lib';
|
|
10
|
-
import { TestEntity } from './testUtils';
|
|
11
|
-
|
|
12
|
-
export class TestErgoExtractorAction extends AbstractErgoExtractorAction<
|
|
13
|
-
AbstractBoxData,
|
|
14
|
-
AbstractErgoExtractorEntity
|
|
15
|
-
> {
|
|
16
|
-
constructor(dataSource: DataSource) {
|
|
17
|
-
super(dataSource, TestEntity);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* create the test database entity from data and block information
|
|
22
|
-
*/
|
|
23
|
-
createEntity = (
|
|
24
|
-
boxes: AbstractBoxData[],
|
|
25
|
-
block: BlockInfo,
|
|
26
|
-
extractor: string,
|
|
27
|
-
): Omit<AbstractErgoExtractorEntity, 'id'>[] => {
|
|
28
|
-
return boxes.map((box) => ({
|
|
29
|
-
boxId: box.boxId,
|
|
30
|
-
block: block.hash,
|
|
31
|
-
height: block.height,
|
|
32
|
-
serialized: box.serialized,
|
|
33
|
-
extractor: extractor,
|
|
34
|
-
}));
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* convert the database entity back to raw data
|
|
39
|
-
*/
|
|
40
|
-
convertEntityToData = (
|
|
41
|
-
entities: AbstractErgoExtractorEntity[],
|
|
42
|
-
): AbstractBoxData[] => {
|
|
43
|
-
return entities.map((data) => pick(data, ['boxId', 'serialized']));
|
|
44
|
-
};
|
|
45
|
-
}
|