@rosen-bridge/abstract-extractor 2.0.3 → 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 +22 -0
- package/dist/abstractExtractor.js +1 -1
- package/dist/ergo/AbstractErgoExtractor.js +2 -3
- package/dist/ergo/AbstractErgoExtractorAction.d.ts +1 -1
- package/dist/ergo/AbstractErgoExtractorAction.d.ts.map +1 -1
- package/dist/ergo/AbstractErgoExtractorAction.js +4 -6
- package/dist/ergo/AbstractErgoExtractorEntity.d.ts.map +1 -1
- package/dist/ergo/AbstractErgoExtractorEntity.js +2 -2
- package/dist/ergo/initializable/AbstractInitializable.js +1 -1
- package/dist/ergo/initializable/AbstractInitializableAction.d.ts +1 -1
- package/dist/ergo/initializable/AbstractInitializableAction.d.ts.map +1 -1
- package/dist/ergo/initializable/AbstractInitializableAction.js +1 -1
- package/dist/ergo/network/AbstractNetwork.js +1 -1
- package/dist/ergo/network/ExplorerNetwork.js +1 -1
- package/dist/ergo/network/NodeNetwork.js +1 -1
- package/package.json +23 -21
- package/.eslintignore +0 -1
- 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 -324
- package/lib/ergo/AbstractErgoExtractorEntity.ts +0 -28
- package/lib/ergo/index.ts +0 -9
- package/lib/ergo/initializable/AbstractInitializable.ts +0 -228
- package/lib/ergo/initializable/AbstractInitializableAction.ts +0 -29
- 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 -285
- package/tests/AbstractErgoExtractorAction.mock.ts +0 -45
- package/tests/AbstractErgoExtractorAction.spec.ts +0 -269
- package/tests/initializable/AbstractInitializable.mock.ts +0 -44
- package/tests/initializable/AbstractInitializable.spec.ts +0 -387
- package/tests/initializable/AbstractInitializableAction.mock.ts +0 -45
- package/tests/initializable/AbstractInitializableAction.spec.ts +0 -65
- package/tests/initializable/testData.ts +0 -283
- package/tests/network/ExplorerNetwork.spec.ts +0 -75
- package/tests/network/NodeNetwork.spec.ts +0 -57
- 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 -8
- package/vitest.config.ts +0 -17
package/package.json
CHANGED
|
@@ -1,48 +1,50 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rosen-bridge/abstract-extractor",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "Rosen Bridge extractor interfaces to work with scanner",
|
|
5
|
-
"repository":
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/rosen-bridge/scanner.git"
|
|
8
|
+
},
|
|
6
9
|
"license": "MIT",
|
|
7
10
|
"author": "Rosen Team",
|
|
8
11
|
"type": "module",
|
|
9
12
|
"main": "dist/index.js",
|
|
10
13
|
"types": "dist/index.d.ts",
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"CHANGELOG.md"
|
|
17
|
+
],
|
|
11
18
|
"scripts": {
|
|
12
19
|
"build": "tsc --build tsconfig.build.json",
|
|
13
20
|
"coverage": "npm run test -- --coverage",
|
|
14
21
|
"lint": "eslint --fix . && npm run prettify",
|
|
15
22
|
"prettify": "prettier --write . --ignore-path ./.gitignore",
|
|
16
23
|
"release": "npm run test -- --run && npm run build && npm publish --access public",
|
|
17
|
-
"test": "NODE_OPTIONS
|
|
24
|
+
"test": "NODE_OPTIONS='--import tsx' vitest",
|
|
18
25
|
"type-check": "tsc --noEmit"
|
|
19
26
|
},
|
|
20
27
|
"devDependencies": {
|
|
21
28
|
"@types/lodash-es": "^4.17.12",
|
|
22
|
-
"@types/node": "^
|
|
29
|
+
"@types/node": "^22.18.0",
|
|
23
30
|
"@types/uuid": "^10.0.0",
|
|
24
|
-
"@
|
|
25
|
-
"@typescript-eslint/parser": "^6.19.1",
|
|
26
|
-
"@vitest/coverage-istanbul": "^1.2.2",
|
|
31
|
+
"@vitest/coverage-istanbul": "^3.1.4",
|
|
27
32
|
"await-semaphore": "^0.1.3",
|
|
28
|
-
"
|
|
29
|
-
"eslint-config-prettier": "^9.1.0",
|
|
30
|
-
"extensionless": "^1.9.6",
|
|
31
|
-
"prettier": "^3.2.4",
|
|
33
|
+
"tsx": "^4.19.4",
|
|
32
34
|
"typescript": "^5.3.3",
|
|
33
|
-
"vitest": "^1.
|
|
34
|
-
},
|
|
35
|
-
"engines": {
|
|
36
|
-
"node": ">=20.11.0"
|
|
35
|
+
"vitest": "^3.1.4"
|
|
37
36
|
},
|
|
38
37
|
"dependencies": {
|
|
39
|
-
"@rosen-bridge/abstract-logger": "^
|
|
40
|
-
"@rosen-bridge/
|
|
41
|
-
"@rosen-bridge/
|
|
42
|
-
"@rosen-
|
|
43
|
-
"@rosen-clients/ergo-
|
|
38
|
+
"@rosen-bridge/abstract-logger": "^3.0.0",
|
|
39
|
+
"@rosen-bridge/extended-typeorm": "^1.0.0",
|
|
40
|
+
"@rosen-bridge/json-bigint": "^1.0.0",
|
|
41
|
+
"@rosen-bridge/scanner-interfaces": "^0.2.0",
|
|
42
|
+
"@rosen-clients/ergo-explorer": "^2.0.0",
|
|
43
|
+
"@rosen-clients/ergo-node": "^3.0.0",
|
|
44
44
|
"lodash-es": "^4.17.21",
|
|
45
|
-
"typeorm": "^0.3.20",
|
|
46
45
|
"uuid": "^9.0.0"
|
|
46
|
+
},
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=22.18.0"
|
|
47
49
|
}
|
|
48
50
|
}
|
package/.eslintignore
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
dist
|
package/lib/abstractExtractor.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { BlockInfo, Block } from '@rosen-bridge/scanner-interfaces';
|
|
2
|
-
|
|
3
|
-
export abstract class AbstractExtractor<TransactionType> {
|
|
4
|
-
/**
|
|
5
|
-
* process a list of transactions in a block and store required information
|
|
6
|
-
* @param txs list of transactions in the block
|
|
7
|
-
* @param block
|
|
8
|
-
* @return true if the process is completed successfully and false otherwise
|
|
9
|
-
*/
|
|
10
|
-
abstract processTransactions: (
|
|
11
|
-
txs: Array<TransactionType>,
|
|
12
|
-
block: Block
|
|
13
|
-
) => Promise<boolean>;
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* return extractor id. This id must be unique over all extractors.
|
|
17
|
-
*/
|
|
18
|
-
abstract getId: () => string;
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* fork one block and remove all stored information for this block
|
|
22
|
-
* @param hash block hash
|
|
23
|
-
*/
|
|
24
|
-
abstract forkBlock: (hash: string) => Promise<void>;
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* initialize extractor database with data created below the initial height
|
|
28
|
-
* @param initialBlock
|
|
29
|
-
*/
|
|
30
|
-
abstract initializeBoxes: (initialBlock: BlockInfo) => Promise<void>;
|
|
31
|
-
}
|
package/lib/constants.ts
DELETED
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
import { AbstractLogger, DummyLogger } from '@rosen-bridge/abstract-logger';
|
|
2
|
-
import JsonBigInt from '@rosen-bridge/json-bigint';
|
|
3
|
-
import { Mutex } from 'await-semaphore';
|
|
4
|
-
import { v4 as uuidv4 } from 'uuid';
|
|
5
|
-
import {
|
|
6
|
-
Transaction,
|
|
7
|
-
OutputBox,
|
|
8
|
-
InputExtension,
|
|
9
|
-
BlockInfo,
|
|
10
|
-
} from '@rosen-bridge/scanner-interfaces';
|
|
11
|
-
|
|
12
|
-
import { AbstractExtractor } from '../abstractExtractor';
|
|
13
|
-
import { AbstractErgoExtractorAction } from './AbstractErgoExtractorAction';
|
|
14
|
-
import {
|
|
15
|
-
AbstractBoxData,
|
|
16
|
-
SpendInfo,
|
|
17
|
-
CallbackType,
|
|
18
|
-
CallbackMap,
|
|
19
|
-
CallbackDataMap,
|
|
20
|
-
TxExtra,
|
|
21
|
-
} from './interfaces';
|
|
22
|
-
import { AbstractErgoExtractorEntity } from './AbstractErgoExtractorEntity';
|
|
23
|
-
|
|
24
|
-
export abstract class AbstractErgoExtractor<
|
|
25
|
-
ExtractedData extends AbstractBoxData,
|
|
26
|
-
ExtractorEntity extends AbstractErgoExtractorEntity
|
|
27
|
-
> extends AbstractExtractor<Transaction> {
|
|
28
|
-
protected abstract actions: AbstractErgoExtractorAction<
|
|
29
|
-
ExtractedData,
|
|
30
|
-
ExtractorEntity
|
|
31
|
-
>;
|
|
32
|
-
protected logger: AbstractLogger;
|
|
33
|
-
protected callbacks: {
|
|
34
|
-
[K in CallbackType]: Map<string, CallbackMap<ExtractedData>[K]>;
|
|
35
|
-
} = {
|
|
36
|
-
[CallbackType.Update]: new Map(),
|
|
37
|
-
[CallbackType.Insert]: new Map(),
|
|
38
|
-
[CallbackType.Delete]: new Map(),
|
|
39
|
-
[CallbackType.Spend]: new Map(),
|
|
40
|
-
};
|
|
41
|
-
private callbackMutex = new Mutex();
|
|
42
|
-
|
|
43
|
-
constructor(logger = new DummyLogger()) {
|
|
44
|
-
super();
|
|
45
|
-
this.logger = logger;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* hook a new callback on a callback type
|
|
50
|
-
* @param type
|
|
51
|
-
* @param id
|
|
52
|
-
* @param callback
|
|
53
|
-
* @returns callback registered id
|
|
54
|
-
*/
|
|
55
|
-
hook = async <T extends CallbackType>(
|
|
56
|
-
type: T,
|
|
57
|
-
callback: CallbackMap<ExtractedData>[T]
|
|
58
|
-
): Promise<string> => {
|
|
59
|
-
const release = await this.callbackMutex.acquire();
|
|
60
|
-
const callbackMap = this.callbacks[type];
|
|
61
|
-
const id = uuidv4();
|
|
62
|
-
callbackMap.set(id, callback);
|
|
63
|
-
release();
|
|
64
|
-
return id;
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* unhook a callback on a type
|
|
69
|
-
* returns false if there is no registered callback with the id
|
|
70
|
-
* @param type
|
|
71
|
-
* @param id
|
|
72
|
-
* @returns success status
|
|
73
|
-
*/
|
|
74
|
-
unhook = async (type: CallbackType, id: string): Promise<boolean> => {
|
|
75
|
-
const release = await this.callbackMutex.acquire();
|
|
76
|
-
const callbackMap = this.callbacks[type];
|
|
77
|
-
if (!callbackMap.has(id)) {
|
|
78
|
-
this.logger.warn(
|
|
79
|
-
`Callback with Id [${id}] is not registered for type [${type}].`
|
|
80
|
-
);
|
|
81
|
-
return false;
|
|
82
|
-
}
|
|
83
|
-
callbackMap.delete(id);
|
|
84
|
-
release();
|
|
85
|
-
return true;
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* trigger all callbacks registered on a specific type with the provided data
|
|
90
|
-
* @param type
|
|
91
|
-
* @param data
|
|
92
|
-
*/
|
|
93
|
-
protected triggerCallbacks<T extends CallbackType>(
|
|
94
|
-
type: T,
|
|
95
|
-
data: CallbackDataMap<ExtractedData>[T]
|
|
96
|
-
): void {
|
|
97
|
-
const callbackMap = this.callbacks[type];
|
|
98
|
-
callbackMap.forEach((callback) => {
|
|
99
|
-
callback(data);
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* extract box data to proper format (not including spending information)
|
|
105
|
-
* @param box
|
|
106
|
-
* @param inputExtensions all input box extensions in transaction
|
|
107
|
-
* @return extracted data in proper format
|
|
108
|
-
*/
|
|
109
|
-
abstract extractBoxData: (
|
|
110
|
-
box: OutputBox,
|
|
111
|
-
inputExtensions: InputExtension[],
|
|
112
|
-
txExtra?: TxExtra
|
|
113
|
-
) => ExtractedData | undefined;
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* check proper data format in the box
|
|
117
|
-
* @param box
|
|
118
|
-
* @return true if the box has the required data and false otherwise
|
|
119
|
-
*/
|
|
120
|
-
abstract hasData: (box: OutputBox) => boolean;
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* create spend info array for the transaction
|
|
124
|
-
* @param tx
|
|
125
|
-
* @returns spend info array of the transaction
|
|
126
|
-
*/
|
|
127
|
-
getTransactionSpendInfo = (tx: Transaction) => {
|
|
128
|
-
let boxIndex = 1;
|
|
129
|
-
const spendInfoArray = [];
|
|
130
|
-
for (const input of tx.inputs) {
|
|
131
|
-
spendInfoArray.push({ txId: tx.id, boxId: input.boxId, index: boxIndex });
|
|
132
|
-
boxIndex += 1;
|
|
133
|
-
}
|
|
134
|
-
return spendInfoArray;
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* extract transaction extra information
|
|
139
|
-
* override this function if there is extra needed information
|
|
140
|
-
* @param tx
|
|
141
|
-
* @returns
|
|
142
|
-
*/
|
|
143
|
-
getTransactionExtraData = (
|
|
144
|
-
tx: Transaction // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
145
|
-
): TxExtra => {
|
|
146
|
-
return {};
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* process a list of transactions in a block and store required information
|
|
151
|
-
* @param txs list of transactions in the block
|
|
152
|
-
* @param block
|
|
153
|
-
* @return true if the process is completed successfully and false otherwise
|
|
154
|
-
*/
|
|
155
|
-
processTransactions = async (
|
|
156
|
-
txs: Transaction[],
|
|
157
|
-
block: BlockInfo
|
|
158
|
-
): Promise<boolean> => {
|
|
159
|
-
try {
|
|
160
|
-
const boxes: Array<ExtractedData> = [];
|
|
161
|
-
const spentInfos: Array<SpendInfo> = [];
|
|
162
|
-
for (const tx of txs) {
|
|
163
|
-
const inputExtensions = tx.inputs.map((input) => input.extension || {});
|
|
164
|
-
for (const output of tx.outputs) {
|
|
165
|
-
if (!this.hasData(output)) {
|
|
166
|
-
continue;
|
|
167
|
-
}
|
|
168
|
-
this.logger.debug(`Trying to extract data from box ${output.boxId}`);
|
|
169
|
-
const extractedData = this.extractBoxData(
|
|
170
|
-
output,
|
|
171
|
-
inputExtensions,
|
|
172
|
-
this.getTransactionExtraData(tx)
|
|
173
|
-
);
|
|
174
|
-
if (extractedData) {
|
|
175
|
-
this.logger.debug(
|
|
176
|
-
`Extracted data ${JsonBigInt.stringify(extractedData)} from box ${
|
|
177
|
-
output.boxId
|
|
178
|
-
}`
|
|
179
|
-
);
|
|
180
|
-
boxes.push(extractedData);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
spentInfos.push(...this.getTransactionSpendInfo(tx));
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
if (boxes.length > 0) {
|
|
187
|
-
if (!(await this.actions.storeBoxes(boxes, block, this.getId()))) {
|
|
188
|
-
this.logger.warn(
|
|
189
|
-
`Data insertion failed for ${this.getId()} at the block ${
|
|
190
|
-
block.height
|
|
191
|
-
}`
|
|
192
|
-
);
|
|
193
|
-
return false;
|
|
194
|
-
}
|
|
195
|
-
this.triggerCallbacks(CallbackType.Insert, boxes);
|
|
196
|
-
}
|
|
197
|
-
const spentData = await this.actions.spendBoxes(
|
|
198
|
-
spentInfos,
|
|
199
|
-
block,
|
|
200
|
-
this.getId()
|
|
201
|
-
);
|
|
202
|
-
if (spentData.length > 0) {
|
|
203
|
-
this.triggerCallbacks(CallbackType.Spend, spentData);
|
|
204
|
-
}
|
|
205
|
-
} catch (e) {
|
|
206
|
-
this.logger.error(
|
|
207
|
-
`Processing transactions failed for ${this.getId()} at the block ${
|
|
208
|
-
block.height
|
|
209
|
-
} with error: ${e}`
|
|
210
|
-
);
|
|
211
|
-
return false;
|
|
212
|
-
}
|
|
213
|
-
return true;
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* fork one block and remove all stored information for this block
|
|
218
|
-
* @param hash block hash
|
|
219
|
-
*/
|
|
220
|
-
forkBlock = async (hash: string): Promise<void> => {
|
|
221
|
-
const result = await this.actions.deleteBlockBoxes(hash, this.getId());
|
|
222
|
-
if (result.deletedData.length > 0)
|
|
223
|
-
this.triggerCallbacks(CallbackType.Delete, result.deletedData);
|
|
224
|
-
if (result.updatedData.length > 0)
|
|
225
|
-
this.triggerCallbacks(CallbackType.Update, result.updatedData);
|
|
226
|
-
};
|
|
227
|
-
}
|
|
@@ -1,324 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DataSource,
|
|
3
|
-
In,
|
|
4
|
-
Repository,
|
|
5
|
-
Not,
|
|
6
|
-
EntityTarget,
|
|
7
|
-
FindOptionsWhere,
|
|
8
|
-
QueryRunner,
|
|
9
|
-
} from 'typeorm';
|
|
10
|
-
import { chunk, difference, pick } from 'lodash-es';
|
|
11
|
-
import { AbstractLogger, DummyLogger } from '@rosen-bridge/abstract-logger';
|
|
12
|
-
import JsonBigInt from '@rosen-bridge/json-bigint';
|
|
13
|
-
import { BlockInfo } from '@rosen-bridge/scanner-interfaces';
|
|
14
|
-
|
|
15
|
-
import { AbstractBoxData, BoxInfo, SpendInfo } from './interfaces';
|
|
16
|
-
import { DB_CHUNK_SIZE } from '../constants';
|
|
17
|
-
import { AbstractErgoExtractorEntity } from './AbstractErgoExtractorEntity';
|
|
18
|
-
import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity';
|
|
19
|
-
|
|
20
|
-
export abstract class AbstractErgoExtractorAction<
|
|
21
|
-
ExtractedData extends AbstractBoxData,
|
|
22
|
-
ExtractorEntity extends AbstractErgoExtractorEntity
|
|
23
|
-
> {
|
|
24
|
-
private readonly datasource: DataSource;
|
|
25
|
-
readonly logger: AbstractLogger;
|
|
26
|
-
protected readonly repository: Repository<ExtractorEntity>;
|
|
27
|
-
private repo: EntityTarget<ExtractorEntity>;
|
|
28
|
-
|
|
29
|
-
constructor(
|
|
30
|
-
dataSource: DataSource,
|
|
31
|
-
repo: EntityTarget<ExtractorEntity>,
|
|
32
|
-
logger?: AbstractLogger
|
|
33
|
-
) {
|
|
34
|
-
this.datasource = dataSource;
|
|
35
|
-
this.logger = logger ? logger : new DummyLogger();
|
|
36
|
-
this.repository = this.datasource.getRepository(repo);
|
|
37
|
-
this.repo = repo;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* create the database entity from extracted data and block information
|
|
42
|
-
*/
|
|
43
|
-
protected createEntity = (
|
|
44
|
-
data: ExtractedData[], // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
45
|
-
block: BlockInfo, // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
46
|
-
extractor: string // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
47
|
-
): Array<Omit<ExtractorEntity, 'id'>> => {
|
|
48
|
-
throw Error(
|
|
49
|
-
'You must implement `createEntity` or override `insertEntities` and `updateEntities`'
|
|
50
|
-
);
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* convert the database entity back to raw data
|
|
55
|
-
*/
|
|
56
|
-
protected convertEntityToData = (
|
|
57
|
-
entities: ExtractorEntity[] // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
58
|
-
): ExtractedData[] => {
|
|
59
|
-
throw Error(
|
|
60
|
-
'You must implement `convertEntityToData` or override `deleteBlockEntities`'
|
|
61
|
-
);
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* insert entities extracted from a block to database
|
|
66
|
-
* @param queryRunner
|
|
67
|
-
* @param boxesToInsert
|
|
68
|
-
* @param block
|
|
69
|
-
* @param extractor
|
|
70
|
-
*/
|
|
71
|
-
protected insertEntities = async (
|
|
72
|
-
queryRunner: QueryRunner,
|
|
73
|
-
boxesToInsert: Array<ExtractedData>,
|
|
74
|
-
block: BlockInfo,
|
|
75
|
-
extractor: string
|
|
76
|
-
) => {
|
|
77
|
-
const repository = queryRunner.manager.getRepository(this.repo);
|
|
78
|
-
await repository.insert(
|
|
79
|
-
this.createEntity(
|
|
80
|
-
boxesToInsert,
|
|
81
|
-
block,
|
|
82
|
-
extractor
|
|
83
|
-
) as unknown as QueryDeepPartialEntity<ExtractorEntity>[]
|
|
84
|
-
);
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* update entities related to a box
|
|
89
|
-
* @param queryRunner
|
|
90
|
-
* @param updateBox
|
|
91
|
-
* @param block
|
|
92
|
-
* @param extractor
|
|
93
|
-
*/
|
|
94
|
-
protected updateEntity = async (
|
|
95
|
-
queryRunner: QueryRunner,
|
|
96
|
-
updateBox: ExtractedData, // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
97
|
-
block: BlockInfo, // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
98
|
-
extractor: string
|
|
99
|
-
) => {
|
|
100
|
-
const repository = queryRunner.manager.getRepository(this.repo);
|
|
101
|
-
const box = this.createEntity([updateBox], block, extractor)[0];
|
|
102
|
-
await repository.update(
|
|
103
|
-
{
|
|
104
|
-
boxId: box.boxId,
|
|
105
|
-
extractor: extractor,
|
|
106
|
-
} as FindOptionsWhere<ExtractorEntity>,
|
|
107
|
-
box as unknown as QueryDeepPartialEntity<ExtractorEntity>
|
|
108
|
-
);
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* delete all data extracted from a block
|
|
113
|
-
* @param queryRunner
|
|
114
|
-
* @param extractor
|
|
115
|
-
* @param block
|
|
116
|
-
* @returns
|
|
117
|
-
*/
|
|
118
|
-
protected deleteBlockEntities = async (
|
|
119
|
-
queryRunner: QueryRunner,
|
|
120
|
-
extractor: string,
|
|
121
|
-
block: string
|
|
122
|
-
): Promise<ExtractedData[]> => {
|
|
123
|
-
const repository = queryRunner.manager.getRepository(this.repo);
|
|
124
|
-
const deletedData = await repository.find({
|
|
125
|
-
where: {
|
|
126
|
-
extractor: extractor,
|
|
127
|
-
block: block,
|
|
128
|
-
} as FindOptionsWhere<ExtractorEntity>,
|
|
129
|
-
});
|
|
130
|
-
await repository.delete({
|
|
131
|
-
extractor: extractor,
|
|
132
|
-
block: block,
|
|
133
|
-
} as unknown as FindOptionsWhere<ExtractorEntity>);
|
|
134
|
-
return this.convertEntityToData(deletedData);
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* delete all data extracted from a block
|
|
139
|
-
* @param queryRunner
|
|
140
|
-
* @param extractor
|
|
141
|
-
* @param block
|
|
142
|
-
* @returns
|
|
143
|
-
*/
|
|
144
|
-
protected updateBlockEntities = async (
|
|
145
|
-
queryRunner: QueryRunner, // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
146
|
-
extractor: string,
|
|
147
|
-
block: string
|
|
148
|
-
): Promise<ExtractorEntity[]> => {
|
|
149
|
-
const repository = this.datasource.getRepository(
|
|
150
|
-
this.repo as EntityTarget<new () => ExtractorEntity>
|
|
151
|
-
);
|
|
152
|
-
const updatedData = await repository.find({
|
|
153
|
-
where: {
|
|
154
|
-
extractor: extractor,
|
|
155
|
-
spendBlock: block,
|
|
156
|
-
block: Not(block),
|
|
157
|
-
} as unknown as FindOptionsWhere<ExtractorEntity>,
|
|
158
|
-
});
|
|
159
|
-
await repository.update(
|
|
160
|
-
{
|
|
161
|
-
spendBlock: block,
|
|
162
|
-
extractor: extractor,
|
|
163
|
-
} as unknown as FindOptionsWhere<ExtractorEntity>,
|
|
164
|
-
{
|
|
165
|
-
spendBlock: null,
|
|
166
|
-
spendHeight: 0,
|
|
167
|
-
}
|
|
168
|
-
);
|
|
169
|
-
return updatedData as unknown as ExtractorEntity[];
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* insert all extracted box data in an atomic transaction
|
|
174
|
-
* update the data if a box with the same id is already stored in db
|
|
175
|
-
* @param boxes
|
|
176
|
-
* @param block
|
|
177
|
-
* @param extractor
|
|
178
|
-
* @return inserted items and updated box ids
|
|
179
|
-
* returns undefined in case of any problem
|
|
180
|
-
*/
|
|
181
|
-
storeBoxes = async (
|
|
182
|
-
boxes: Array<ExtractedData>,
|
|
183
|
-
block: BlockInfo,
|
|
184
|
-
extractor: string
|
|
185
|
-
): Promise<boolean> => {
|
|
186
|
-
let boxesToInsert: ExtractedData[] = [],
|
|
187
|
-
boxesToUpdate: ExtractedData[] = [];
|
|
188
|
-
const queryRunner = this.datasource.createQueryRunner();
|
|
189
|
-
await queryRunner.connect();
|
|
190
|
-
await queryRunner.startTransaction();
|
|
191
|
-
try {
|
|
192
|
-
const repository = queryRunner.manager.getRepository(this.repo);
|
|
193
|
-
const dbBoxIds = (
|
|
194
|
-
await repository.findBy({
|
|
195
|
-
boxId: In(boxes.map((item) => item.boxId)),
|
|
196
|
-
extractor: extractor,
|
|
197
|
-
} as FindOptionsWhere<ExtractorEntity>)
|
|
198
|
-
).map((box) => box.boxId);
|
|
199
|
-
if (dbBoxIds.length > 0)
|
|
200
|
-
this.logger.debug(`Found stored boxes with same boxId`, dbBoxIds);
|
|
201
|
-
|
|
202
|
-
boxesToUpdate = boxes.filter((box) => dbBoxIds.includes(box.boxId));
|
|
203
|
-
boxesToInsert = difference(boxes, boxesToUpdate);
|
|
204
|
-
|
|
205
|
-
if (boxesToInsert.length > 0) {
|
|
206
|
-
this.logger.debug(`Inserting boxes`);
|
|
207
|
-
await this.insertEntities(queryRunner, boxesToInsert, block, extractor);
|
|
208
|
-
}
|
|
209
|
-
if (boxesToUpdate.length > 0)
|
|
210
|
-
this.logger.info(
|
|
211
|
-
`Updating boxes with following Ids in the database: [${boxesToUpdate
|
|
212
|
-
.map((col) => col.boxId)
|
|
213
|
-
.join(', ')}]`
|
|
214
|
-
);
|
|
215
|
-
for (const box of boxesToUpdate) {
|
|
216
|
-
this.logger.debug(
|
|
217
|
-
`Updating boxes in database [${JsonBigInt.stringify(box)}]`
|
|
218
|
-
);
|
|
219
|
-
await this.updateEntity(queryRunner, box, block, extractor);
|
|
220
|
-
}
|
|
221
|
-
await queryRunner.commitTransaction();
|
|
222
|
-
return true;
|
|
223
|
-
} catch (e) {
|
|
224
|
-
this.logger.error(`An error occurred during store boxes action: ${e}`);
|
|
225
|
-
await queryRunner.rollbackTransaction();
|
|
226
|
-
return false;
|
|
227
|
-
} finally {
|
|
228
|
-
await queryRunner.release();
|
|
229
|
-
}
|
|
230
|
-
};
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* update spending information of stored boxes
|
|
234
|
-
* chunk spendInfos to prevent large database queries
|
|
235
|
-
* Note: It only updates the spendHeight and spendBlock fields. If updating
|
|
236
|
-
* anything else is required, override this implementation to include the
|
|
237
|
-
* additional fields.
|
|
238
|
-
* @param spendInfos
|
|
239
|
-
* @param block
|
|
240
|
-
* @param extractor
|
|
241
|
-
* @returns spent box ids
|
|
242
|
-
*/
|
|
243
|
-
spendBoxes = async (
|
|
244
|
-
spendInfos: Array<SpendInfo>,
|
|
245
|
-
block: BlockInfo,
|
|
246
|
-
extractor: string
|
|
247
|
-
): Promise<BoxInfo[]> => {
|
|
248
|
-
const spentData = [];
|
|
249
|
-
const spendInfoChunks = chunk(spendInfos, DB_CHUNK_SIZE);
|
|
250
|
-
for (const spendInfoChunk of spendInfoChunks) {
|
|
251
|
-
const boxIds = spendInfoChunk.map((info) => info.boxId);
|
|
252
|
-
const updateResult = await this.repository.update(
|
|
253
|
-
{
|
|
254
|
-
boxId: In(boxIds),
|
|
255
|
-
extractor: extractor,
|
|
256
|
-
} as FindOptionsWhere<ExtractorEntity>,
|
|
257
|
-
{
|
|
258
|
-
spendBlock: block.hash,
|
|
259
|
-
spendHeight: block.height,
|
|
260
|
-
} as unknown as QueryDeepPartialEntity<ExtractorEntity>
|
|
261
|
-
);
|
|
262
|
-
|
|
263
|
-
if (updateResult.affected && updateResult.affected > 0) {
|
|
264
|
-
const spentRows = await this.repository.findBy({
|
|
265
|
-
boxId: In(boxIds),
|
|
266
|
-
spendBlock: block.hash,
|
|
267
|
-
} as FindOptionsWhere<ExtractorEntity>);
|
|
268
|
-
spentData.push(...spentRows);
|
|
269
|
-
for (const row of spentRows) {
|
|
270
|
-
this.logger.debug(
|
|
271
|
-
`Spent box with boxId [${row.boxId}] at height ${block.height}`
|
|
272
|
-
);
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
return spentData.map((data) => pick(data, 'boxId'));
|
|
277
|
-
};
|
|
278
|
-
|
|
279
|
-
/**
|
|
280
|
-
* delete extracted data from a specific block
|
|
281
|
-
* if a box is spend in this block mark it as unspent
|
|
282
|
-
* if a box is created in this block remove it from database
|
|
283
|
-
* @param block
|
|
284
|
-
* @param extractor
|
|
285
|
-
* @return deleted items and updated box ids
|
|
286
|
-
*/
|
|
287
|
-
deleteBlockBoxes = async (
|
|
288
|
-
block: string,
|
|
289
|
-
extractor: string
|
|
290
|
-
): Promise<{ deletedData: ExtractedData[]; updatedData: BoxInfo[] }> => {
|
|
291
|
-
const queryRunner = this.datasource.createQueryRunner();
|
|
292
|
-
await queryRunner.connect();
|
|
293
|
-
await queryRunner.startTransaction();
|
|
294
|
-
try {
|
|
295
|
-
this.logger.info(
|
|
296
|
-
`Deleting boxes in block ${block} and extractor ${extractor}`
|
|
297
|
-
);
|
|
298
|
-
const updatedData = await this.updateBlockEntities(
|
|
299
|
-
queryRunner,
|
|
300
|
-
extractor,
|
|
301
|
-
block
|
|
302
|
-
);
|
|
303
|
-
const deletedData = await this.deleteBlockEntities(
|
|
304
|
-
queryRunner,
|
|
305
|
-
extractor,
|
|
306
|
-
block
|
|
307
|
-
);
|
|
308
|
-
await queryRunner.commitTransaction();
|
|
309
|
-
return {
|
|
310
|
-
deletedData,
|
|
311
|
-
updatedData: updatedData.map((data) => pick(data, 'boxId')),
|
|
312
|
-
};
|
|
313
|
-
} catch (error) {
|
|
314
|
-
await queryRunner.rollbackTransaction();
|
|
315
|
-
this.logger.error(
|
|
316
|
-
`An error occurred while deleting data extracted from block ${block}`,
|
|
317
|
-
error
|
|
318
|
-
);
|
|
319
|
-
throw error;
|
|
320
|
-
} finally {
|
|
321
|
-
await queryRunner.release();
|
|
322
|
-
}
|
|
323
|
-
};
|
|
324
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { Column, PrimaryGeneratedColumn, Unique } from 'typeorm';
|
|
2
|
-
|
|
3
|
-
@Unique(['boxId', 'extractor'])
|
|
4
|
-
export abstract class AbstractErgoExtractorEntity {
|
|
5
|
-
@PrimaryGeneratedColumn()
|
|
6
|
-
id: number;
|
|
7
|
-
|
|
8
|
-
@Column({ type: 'varchar' })
|
|
9
|
-
boxId: string;
|
|
10
|
-
|
|
11
|
-
@Column({ type: 'varchar' })
|
|
12
|
-
block: string;
|
|
13
|
-
|
|
14
|
-
@Column({ type: 'int' })
|
|
15
|
-
height: number;
|
|
16
|
-
|
|
17
|
-
@Column({ nullable: true, type: 'varchar' })
|
|
18
|
-
spendBlock?: string | null;
|
|
19
|
-
|
|
20
|
-
@Column({ nullable: true, type: 'int' })
|
|
21
|
-
spendHeight?: number | null;
|
|
22
|
-
|
|
23
|
-
@Column({ type: 'varchar' })
|
|
24
|
-
extractor: string;
|
|
25
|
-
|
|
26
|
-
@Column({ type: 'varchar' })
|
|
27
|
-
serialized: string;
|
|
28
|
-
}
|
package/lib/ergo/index.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export * from './AbstractErgoExtractor';
|
|
2
|
-
export * from './AbstractErgoExtractorAction';
|
|
3
|
-
export * from './interfaces';
|
|
4
|
-
export * from './network/ExplorerNetwork';
|
|
5
|
-
export * from './network/NodeNetwork';
|
|
6
|
-
export * from './network/AbstractNetwork';
|
|
7
|
-
export * from './initializable';
|
|
8
|
-
export * from './utils';
|
|
9
|
-
export * from './AbstractErgoExtractorEntity';
|