@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,228 +0,0 @@
|
|
|
1
|
-
import { AbstractLogger } from '@rosen-bridge/abstract-logger';
|
|
2
|
-
import { groupBy, sortBy } from 'lodash-es';
|
|
3
|
-
import { BlockInfo, ErgoNetworkType } from '@rosen-bridge/scanner-interfaces';
|
|
4
|
-
|
|
5
|
-
import { AbstractBoxData, ExtendedTransaction } from '../interfaces';
|
|
6
|
-
import { API_LIMIT, RETRIAL_COUNT } from '../../constants';
|
|
7
|
-
import { AbstractErgoExtractor } from '../AbstractErgoExtractor';
|
|
8
|
-
import { AbstractInitializableErgoExtractorAction } from './AbstractInitializableAction';
|
|
9
|
-
import { ExplorerNetwork } from '../network/ExplorerNetwork';
|
|
10
|
-
import { NodeNetwork } from '../network/NodeNetwork';
|
|
11
|
-
import { AbstractErgoExtractorEntity } from '../AbstractErgoExtractorEntity';
|
|
12
|
-
|
|
13
|
-
export abstract class AbstractInitializableErgoExtractor<
|
|
14
|
-
ExtractedData extends AbstractBoxData,
|
|
15
|
-
ExtractorEntity extends AbstractErgoExtractorEntity,
|
|
16
|
-
> extends AbstractErgoExtractor<ExtractedData, ExtractorEntity> {
|
|
17
|
-
protected initialize: boolean;
|
|
18
|
-
private address: string;
|
|
19
|
-
protected abstract actions: AbstractInitializableErgoExtractorAction<
|
|
20
|
-
ExtractedData,
|
|
21
|
-
ExtractorEntity
|
|
22
|
-
>;
|
|
23
|
-
|
|
24
|
-
private network: ExplorerNetwork | NodeNetwork;
|
|
25
|
-
|
|
26
|
-
constructor(
|
|
27
|
-
type: ErgoNetworkType,
|
|
28
|
-
url: string,
|
|
29
|
-
address: string,
|
|
30
|
-
logger?: AbstractLogger,
|
|
31
|
-
initialize = true,
|
|
32
|
-
) {
|
|
33
|
-
super(logger);
|
|
34
|
-
this.address = address;
|
|
35
|
-
this.initialize = initialize;
|
|
36
|
-
if (type == ErgoNetworkType.Explorer) {
|
|
37
|
-
this.network = new ExplorerNetwork(url);
|
|
38
|
-
this.initializeBoxes = (initialBlock: BlockInfo) => {
|
|
39
|
-
return this.initializeWithExplorer(initialBlock);
|
|
40
|
-
};
|
|
41
|
-
} else if (type == ErgoNetworkType.Node) {
|
|
42
|
-
this.network = new NodeNetwork(url);
|
|
43
|
-
this.initializeBoxes = (initialBlock: BlockInfo) => {
|
|
44
|
-
return this.initializeWithNode(initialBlock);
|
|
45
|
-
};
|
|
46
|
-
} else throw new Error(`Network type ${type} is not supported`);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Initialize extractor using Explorer network
|
|
51
|
-
* @param initialBlock
|
|
52
|
-
*/
|
|
53
|
-
private initializeWithExplorer = async (initialBlock: BlockInfo) => {
|
|
54
|
-
const explorerNetwork = this.network as ExplorerNetwork;
|
|
55
|
-
let fromHeight = 0,
|
|
56
|
-
toHeight = initialBlock.height;
|
|
57
|
-
await this.initWithRetrial(async () => {
|
|
58
|
-
while (fromHeight < toHeight) {
|
|
59
|
-
let txs: Array<ExtendedTransaction>;
|
|
60
|
-
// eslint-disable-next-line no-constant-condition
|
|
61
|
-
while (true) {
|
|
62
|
-
txs = await explorerNetwork.getAddressTransactionsWithHeight(
|
|
63
|
-
this.address,
|
|
64
|
-
fromHeight,
|
|
65
|
-
toHeight,
|
|
66
|
-
);
|
|
67
|
-
this.logger.debug(
|
|
68
|
-
`Found ${txs.length} transactions for the address from height ${fromHeight} to height ${toHeight}`,
|
|
69
|
-
);
|
|
70
|
-
if (txs.length < API_LIMIT || fromHeight === toHeight) {
|
|
71
|
-
break; // Exit loop if we have fewer transactions than the limit or if the range is reduced to a single height
|
|
72
|
-
}
|
|
73
|
-
toHeight = Math.floor((toHeight - fromHeight) / 2) + fromHeight;
|
|
74
|
-
this.logger.debug(
|
|
75
|
-
`Limiting the query height range to [${fromHeight}, ${toHeight}]`,
|
|
76
|
-
);
|
|
77
|
-
}
|
|
78
|
-
if (txs.length < API_LIMIT) {
|
|
79
|
-
if (txs.length > 0) await this.processTransactionBatch(txs);
|
|
80
|
-
} else {
|
|
81
|
-
this.logger.debug(
|
|
82
|
-
`Block at height ${fromHeight} has more than (or equal) ${API_LIMIT} relevant txs, processing all txs in the block`,
|
|
83
|
-
);
|
|
84
|
-
const blockId = await explorerNetwork.getBlockIdAtHeight(fromHeight);
|
|
85
|
-
const blockTxs = await explorerNetwork.getBlockTxs(blockId);
|
|
86
|
-
this.logger.debug(
|
|
87
|
-
`Found ${blockTxs.length} transactions at height ${fromHeight}`,
|
|
88
|
-
);
|
|
89
|
-
await this.processTransactions(blockTxs, {
|
|
90
|
-
hash: blockId,
|
|
91
|
-
height: fromHeight,
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
fromHeight = toHeight + 1;
|
|
95
|
-
toHeight = initialBlock.height;
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Get the total tx count from Node network
|
|
102
|
-
* @returns total tx count of the address
|
|
103
|
-
*/
|
|
104
|
-
private getTotalTxCount = async () => {
|
|
105
|
-
const response = await (
|
|
106
|
-
this.network as NodeNetwork
|
|
107
|
-
).getAddressTransactionsWithOffsetLimit(this.address, 0, 0);
|
|
108
|
-
return response.total;
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Initialize extractor using Node network
|
|
113
|
-
* @param initialBlock
|
|
114
|
-
*/
|
|
115
|
-
private initializeWithNode = async (initialBlock: BlockInfo) => {
|
|
116
|
-
const txCountBeforeInit = await this.getTotalTxCount();
|
|
117
|
-
let offset = 0,
|
|
118
|
-
total = 1,
|
|
119
|
-
round = 1;
|
|
120
|
-
await this.initWithRetrial(async () => {
|
|
121
|
-
// Repeat the whole process twice to cover all spent boxes
|
|
122
|
-
// After round 1 all boxes have been saved and processed once
|
|
123
|
-
// After round 2 spending information of all stored boxes are updated successfully
|
|
124
|
-
while (round <= 2) {
|
|
125
|
-
this.logger.debug(`Starting round ${round} of initialization`);
|
|
126
|
-
while (offset < total) {
|
|
127
|
-
const response = await (
|
|
128
|
-
this.network as NodeNetwork
|
|
129
|
-
).getAddressTransactionsWithOffsetLimit(
|
|
130
|
-
this.address,
|
|
131
|
-
offset,
|
|
132
|
-
API_LIMIT,
|
|
133
|
-
);
|
|
134
|
-
total = response.total;
|
|
135
|
-
const txs = response.items.filter(
|
|
136
|
-
(tx) => tx.inclusionHeight <= initialBlock.height,
|
|
137
|
-
);
|
|
138
|
-
this.logger.debug(
|
|
139
|
-
`Found ${txs.length} transactions below the initial height with offset ${offset} and total number of transactions ${total}`,
|
|
140
|
-
);
|
|
141
|
-
if (txs.length > 0) await this.processTransactionBatch(txs);
|
|
142
|
-
offset += API_LIMIT;
|
|
143
|
-
}
|
|
144
|
-
round++;
|
|
145
|
-
offset = 0; // next round initial offset
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
const txCountAfterInit = await this.getTotalTxCount();
|
|
149
|
-
if (txCountAfterInit != txCountBeforeInit) {
|
|
150
|
-
throw Error(
|
|
151
|
-
'Total transaction count changed during initialization phase, the stored data is not valid',
|
|
152
|
-
);
|
|
153
|
-
}
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Process a batch of transactions
|
|
158
|
-
* group txs into blocks and process them using `processTransactions`
|
|
159
|
-
* @param txs
|
|
160
|
-
*/
|
|
161
|
-
private processTransactionBatch = async (txs: Array<ExtendedTransaction>) => {
|
|
162
|
-
txs = sortBy(txs, (tx) => tx.inclusionHeight);
|
|
163
|
-
const groupedTxs = groupBy(txs, (tx) => tx.blockId);
|
|
164
|
-
this.logger.debug(
|
|
165
|
-
`The transaction batch grouped to ${
|
|
166
|
-
Object.keys(groupedTxs).length
|
|
167
|
-
} blocks`,
|
|
168
|
-
);
|
|
169
|
-
for (const blockId in groupedTxs) {
|
|
170
|
-
const blockTxs = groupedTxs[blockId];
|
|
171
|
-
const block = { hash: blockId, height: blockTxs[0].inclusionHeight };
|
|
172
|
-
this.logger.debug(
|
|
173
|
-
`Processing transactions at height ${blockTxs[0].inclusionHeight}`,
|
|
174
|
-
);
|
|
175
|
-
const success = await this.processTransactions(blockTxs, block);
|
|
176
|
-
if (!success)
|
|
177
|
-
throw Error(
|
|
178
|
-
`Processing transactions failed at height ${blockTxs[0].inclusionHeight}`,
|
|
179
|
-
);
|
|
180
|
-
}
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Initialize the extractor with retrial on any unexpected problem
|
|
185
|
-
* its the common part of initialize with Node and Explorer network
|
|
186
|
-
* @param job
|
|
187
|
-
*/
|
|
188
|
-
private initWithRetrial = async (job: () => Promise<void>) => {
|
|
189
|
-
let trial = 1;
|
|
190
|
-
if (this.initialize) {
|
|
191
|
-
this.logger.debug(
|
|
192
|
-
`Initializing ${this.getId()} started, removing all existing data`,
|
|
193
|
-
);
|
|
194
|
-
await this.actions.removeAllData(this.getId());
|
|
195
|
-
while (trial <= RETRIAL_COUNT) {
|
|
196
|
-
try {
|
|
197
|
-
await job();
|
|
198
|
-
break;
|
|
199
|
-
} catch (e) {
|
|
200
|
-
this.logger.warn(
|
|
201
|
-
`Initialization for ${this.getId()} failed with error :${e}`,
|
|
202
|
-
);
|
|
203
|
-
if (trial == RETRIAL_COUNT)
|
|
204
|
-
throw Error(
|
|
205
|
-
`Initialization for ${this.getId()} failed after ${RETRIAL_COUNT} retrial`,
|
|
206
|
-
);
|
|
207
|
-
trial += 1;
|
|
208
|
-
this.logger.info(
|
|
209
|
-
`Trying again to initialize ${this.getId()} with trial step ${trial}`,
|
|
210
|
-
);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
this.logger.info(
|
|
214
|
-
`Initialization completed successfully for ${this.getId()}`,
|
|
215
|
-
);
|
|
216
|
-
} else {
|
|
217
|
-
this.logger.info(`Initialization for ${this.getId()} is turned off`);
|
|
218
|
-
}
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* initialize extractor database with data created below the initial height
|
|
223
|
-
* ignore initialization if this feature is off
|
|
224
|
-
* try to get data multiple times to pass accidental network problems
|
|
225
|
-
* @param initialBlock
|
|
226
|
-
*/
|
|
227
|
-
initializeBoxes: (initialBlock: BlockInfo) => Promise<void>;
|
|
228
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DataSource,
|
|
3
|
-
EntityTarget,
|
|
4
|
-
FindOptionsWhere,
|
|
5
|
-
} from '@rosen-bridge/extended-typeorm';
|
|
6
|
-
import { AbstractLogger } from '@rosen-bridge/abstract-logger';
|
|
7
|
-
|
|
8
|
-
import { AbstractErgoExtractorAction } from '../AbstractErgoExtractorAction';
|
|
9
|
-
import { AbstractBoxData } from '../interfaces';
|
|
10
|
-
import { AbstractErgoExtractorEntity } from '../AbstractErgoExtractorEntity';
|
|
11
|
-
|
|
12
|
-
export abstract class AbstractInitializableErgoExtractorAction<
|
|
13
|
-
ExtractedData extends AbstractBoxData,
|
|
14
|
-
ExtractorEntity extends AbstractErgoExtractorEntity,
|
|
15
|
-
> extends AbstractErgoExtractorAction<ExtractedData, ExtractorEntity> {
|
|
16
|
-
constructor(
|
|
17
|
-
dataSource: DataSource,
|
|
18
|
-
repo: EntityTarget<ExtractorEntity>,
|
|
19
|
-
logger?: AbstractLogger,
|
|
20
|
-
) {
|
|
21
|
-
super(dataSource, repo, logger);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* remove all existing data for the extractor
|
|
26
|
-
* @param extractorId
|
|
27
|
-
*/
|
|
28
|
-
removeAllData = async (extractorId: string) => {
|
|
29
|
-
await this.repository.delete({
|
|
30
|
-
extractor: extractorId,
|
|
31
|
-
} as FindOptionsWhere<ExtractorEntity>);
|
|
32
|
-
};
|
|
33
|
-
}
|
package/lib/ergo/interfaces.ts
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { OutputBox, Transaction } from '@rosen-bridge/scanner-interfaces';
|
|
2
|
-
|
|
3
|
-
export interface ErgoBox extends OutputBox {
|
|
4
|
-
blockId: string;
|
|
5
|
-
inclusionHeight: number;
|
|
6
|
-
spentBlockId?: string;
|
|
7
|
-
spentHeight?: number;
|
|
8
|
-
spentTransactionId?: string;
|
|
9
|
-
spentIndex?: number;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface ExtendedTransaction extends Transaction {
|
|
13
|
-
inclusionHeight: number;
|
|
14
|
-
blockId: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface SpendInfo {
|
|
18
|
-
boxId: string;
|
|
19
|
-
txId: string;
|
|
20
|
-
index: number;
|
|
21
|
-
extras?: { [key: string]: string };
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface AbstractBoxData {
|
|
25
|
-
boxId: string;
|
|
26
|
-
serialized: string;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export enum CallbackType {
|
|
30
|
-
Insert = 'insert',
|
|
31
|
-
Update = 'update',
|
|
32
|
-
Spend = 'spend',
|
|
33
|
-
Delete = 'delete',
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export interface BoxInfo {
|
|
37
|
-
boxId: string;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export type CallbackDataMap<ExtractedData extends AbstractBoxData> = {
|
|
41
|
-
[CallbackType.Update]: BoxInfo[];
|
|
42
|
-
[CallbackType.Insert]: ExtractedData[];
|
|
43
|
-
[CallbackType.Delete]: ExtractedData[];
|
|
44
|
-
[CallbackType.Spend]: BoxInfo[];
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
export type CallbackMap<ExtractedData extends AbstractBoxData> = {
|
|
48
|
-
[K in CallbackType]: (data: CallbackDataMap<ExtractedData>[K]) => void;
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
export type TxExtra = { [key: string]: string };
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { ErgoBox } from '../interfaces';
|
|
2
|
-
|
|
3
|
-
export abstract class AbstractNetwork {
|
|
4
|
-
/**
|
|
5
|
-
* return related boxes by specified address with limit offset
|
|
6
|
-
* @param address
|
|
7
|
-
* @param offset
|
|
8
|
-
* @param limit
|
|
9
|
-
* @returns related boxes
|
|
10
|
-
*/
|
|
11
|
-
abstract getBoxesByAddress: (
|
|
12
|
-
address: string,
|
|
13
|
-
offset: number,
|
|
14
|
-
limit: number,
|
|
15
|
-
) => Promise<{ boxes: ErgoBox[]; hasNextBatch: boolean }>;
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* return related boxes by specified token with limit offset
|
|
19
|
-
* @param tokenId
|
|
20
|
-
* @param offset
|
|
21
|
-
* @param limit
|
|
22
|
-
* @returns related boxes
|
|
23
|
-
*/
|
|
24
|
-
abstract getBoxesByTokenId: (
|
|
25
|
-
tokenId: string,
|
|
26
|
-
offset: number,
|
|
27
|
-
limit: number,
|
|
28
|
-
) => Promise<{ boxes: ErgoBox[]; hasNextBatch: boolean }>;
|
|
29
|
-
}
|
|
@@ -1,237 +0,0 @@
|
|
|
1
|
-
import ergoExplorerClientFactory from '@rosen-clients/ergo-explorer';
|
|
2
|
-
import { AbstractNetwork } from './AbstractNetwork';
|
|
3
|
-
import { V1 } from '@rosen-clients/ergo-explorer';
|
|
4
|
-
import { BlockInfo, Transaction } from '@rosen-bridge/scanner-interfaces';
|
|
5
|
-
|
|
6
|
-
import { ErgoBox, ExtendedTransaction } from '../interfaces';
|
|
7
|
-
import { mapValues, pick } from 'lodash-es';
|
|
8
|
-
import { API_LIMIT } from '../../constants';
|
|
9
|
-
|
|
10
|
-
export class ExplorerNetwork extends AbstractNetwork {
|
|
11
|
-
private api;
|
|
12
|
-
|
|
13
|
-
constructor(url: string) {
|
|
14
|
-
super();
|
|
15
|
-
this.api = ergoExplorerClientFactory(url);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* return spending information of a specified box by having spendTxId
|
|
20
|
-
* @param boxId
|
|
21
|
-
* @param spendTxId
|
|
22
|
-
*/
|
|
23
|
-
getSpendingInfo = async (
|
|
24
|
-
boxId: string,
|
|
25
|
-
spendTxId: string,
|
|
26
|
-
): Promise<BlockInfo & { spendIndex: number }> => {
|
|
27
|
-
const tx = await this.api.v1.getApiV1TransactionsP1(spendTxId);
|
|
28
|
-
const spendIndex = tx.inputs?.findIndex((box) => box.boxId === boxId);
|
|
29
|
-
if (spendIndex == undefined)
|
|
30
|
-
throw Error(
|
|
31
|
-
`Impossible behavior, the box [${boxId}] should have been spent in tx [${spendTxId}]`,
|
|
32
|
-
);
|
|
33
|
-
return {
|
|
34
|
-
hash: tx.blockId,
|
|
35
|
-
height: tx.inclusionHeight,
|
|
36
|
-
spendIndex,
|
|
37
|
-
};
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* convert explorer api boxes to ErgoBox interface
|
|
42
|
-
* @param box
|
|
43
|
-
* @returns ErgoBox
|
|
44
|
-
*/
|
|
45
|
-
private convertBox = async (box: V1.OutputInfo): Promise<ErgoBox> => {
|
|
46
|
-
const spendInfo = box.spentTransactionId
|
|
47
|
-
? await this.getSpendingInfo(box.boxId, box.spentTransactionId)
|
|
48
|
-
: undefined;
|
|
49
|
-
return {
|
|
50
|
-
blockId: box.blockId,
|
|
51
|
-
boxId: box.boxId,
|
|
52
|
-
creationHeight: box.creationHeight,
|
|
53
|
-
inclusionHeight: box.settlementHeight,
|
|
54
|
-
ergoTree: box.ergoTree,
|
|
55
|
-
index: box.index,
|
|
56
|
-
transactionId: box.transactionId,
|
|
57
|
-
value: box.value,
|
|
58
|
-
additionalRegisters: mapValues(
|
|
59
|
-
box.additionalRegisters,
|
|
60
|
-
'serializedValue',
|
|
61
|
-
),
|
|
62
|
-
assets:
|
|
63
|
-
box.assets?.map((asset) => pick(asset, ['tokenId', 'amount'])) ?? [],
|
|
64
|
-
spentHeight: spendInfo?.height,
|
|
65
|
-
spentBlockId: spendInfo?.hash,
|
|
66
|
-
spentTransactionId: box.spentTransactionId,
|
|
67
|
-
spentIndex: spendInfo?.spendIndex,
|
|
68
|
-
};
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* convert explorer transaction to extractor transaction type
|
|
73
|
-
* @param tx
|
|
74
|
-
*/
|
|
75
|
-
private convertTransaction = (
|
|
76
|
-
tx: V1.TransactionInfo,
|
|
77
|
-
): ExtendedTransaction => {
|
|
78
|
-
return {
|
|
79
|
-
id: tx.id,
|
|
80
|
-
inclusionHeight: tx.inclusionHeight,
|
|
81
|
-
blockId: tx.blockId,
|
|
82
|
-
dataInputs:
|
|
83
|
-
tx.dataInputs?.map((dataInput) => ({
|
|
84
|
-
boxId: dataInput.boxId,
|
|
85
|
-
})) ?? [],
|
|
86
|
-
// TODO: Add input extension to explorer local/ergo/rosen-bridge/scanner/-/issues/156
|
|
87
|
-
inputs: tx.inputs?.map((input) => ({ boxId: input.boxId })) ?? [],
|
|
88
|
-
outputs:
|
|
89
|
-
tx.outputs?.map((output) => ({
|
|
90
|
-
boxId: output.boxId,
|
|
91
|
-
transactionId: output.transactionId,
|
|
92
|
-
additionalRegisters: mapValues(
|
|
93
|
-
output.additionalRegisters,
|
|
94
|
-
'serializedValue',
|
|
95
|
-
),
|
|
96
|
-
assets:
|
|
97
|
-
output.assets?.map((asset) => pick(asset, ['tokenId', 'amount'])) ??
|
|
98
|
-
[],
|
|
99
|
-
ergoTree: output.ergoTree,
|
|
100
|
-
creationHeight: output.creationHeight,
|
|
101
|
-
index: output.index,
|
|
102
|
-
value: output.value,
|
|
103
|
-
})) ?? [],
|
|
104
|
-
};
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* convert explorer block transaction to transaction type
|
|
109
|
-
* @param tx
|
|
110
|
-
*/
|
|
111
|
-
private convertBlockTransaction = (tx: V1.TransactionInfo1): Transaction => {
|
|
112
|
-
return {
|
|
113
|
-
id: tx.id,
|
|
114
|
-
dataInputs:
|
|
115
|
-
tx.dataInputs?.map((dataInput) => ({
|
|
116
|
-
boxId: dataInput.id,
|
|
117
|
-
})) ?? [],
|
|
118
|
-
// TODO: Add input extension local/ergo/rosen-bridge/scanner/-/issues/156
|
|
119
|
-
inputs: tx.inputs?.map((input) => ({ boxId: input.id })) ?? [],
|
|
120
|
-
outputs:
|
|
121
|
-
tx.outputs?.map((output) => ({
|
|
122
|
-
boxId: output.id,
|
|
123
|
-
transactionId: output.txId,
|
|
124
|
-
additionalRegisters: output.additionalRegisters,
|
|
125
|
-
assets:
|
|
126
|
-
output.assets?.map((asset) => pick(asset, ['tokenId', 'amount'])) ??
|
|
127
|
-
[],
|
|
128
|
-
ergoTree: output.ergoTree,
|
|
129
|
-
creationHeight: output.creationHeight,
|
|
130
|
-
index: output.index,
|
|
131
|
-
value: output.value,
|
|
132
|
-
})) ?? [],
|
|
133
|
-
};
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* use explorer api to return related transactions of the specified address in the height range
|
|
138
|
-
* @param tokenId
|
|
139
|
-
* @param offset
|
|
140
|
-
* @param limit
|
|
141
|
-
* @returns related transactions
|
|
142
|
-
*/
|
|
143
|
-
getAddressTransactionsWithHeight = async (
|
|
144
|
-
address: string,
|
|
145
|
-
fromHeight: number,
|
|
146
|
-
toHeight: number,
|
|
147
|
-
): Promise<Array<ExtendedTransaction>> => {
|
|
148
|
-
const txs = await this.api.v1.getApiV1AddressesP1Transactions(address, {
|
|
149
|
-
fromHeight,
|
|
150
|
-
toHeight,
|
|
151
|
-
limit: API_LIMIT,
|
|
152
|
-
});
|
|
153
|
-
if (!txs.items)
|
|
154
|
-
throw new Error(
|
|
155
|
-
'Explorer AddressTransactions api expected to have items',
|
|
156
|
-
);
|
|
157
|
-
return txs.items.map((tx) => this.convertTransaction(tx));
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* use explorer api to get the block id at the specified height
|
|
162
|
-
* @param height
|
|
163
|
-
* @returns block id
|
|
164
|
-
*/
|
|
165
|
-
getBlockIdAtHeight = async (height: number): Promise<string> => {
|
|
166
|
-
const id = await this.api.v0.getApiV0BlocksAtP1(height);
|
|
167
|
-
return id[0];
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* use explorer api to return all transactions in a block
|
|
172
|
-
* @param blockId
|
|
173
|
-
* @returns converted transactions
|
|
174
|
-
*/
|
|
175
|
-
getBlockTxs = async (blockId: string): Promise<Array<Transaction>> => {
|
|
176
|
-
const block = await this.api.v1.getApiV1BlocksP1(blockId);
|
|
177
|
-
if (!block.block.blockTransactions) {
|
|
178
|
-
throw new Error(
|
|
179
|
-
`Expected explorer block api to include block transactions for block ${blockId}`,
|
|
180
|
-
);
|
|
181
|
-
}
|
|
182
|
-
return block.block.blockTransactions.map((tx) =>
|
|
183
|
-
this.convertBlockTransaction(tx),
|
|
184
|
-
);
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* use explorer api to return related boxes by specified address
|
|
189
|
-
* @param address
|
|
190
|
-
* @param offset
|
|
191
|
-
* @param limit
|
|
192
|
-
* @returns related boxes
|
|
193
|
-
*/
|
|
194
|
-
getBoxesByAddress = async (
|
|
195
|
-
address: string,
|
|
196
|
-
offset: number,
|
|
197
|
-
limit: number,
|
|
198
|
-
): Promise<{ boxes: ErgoBox[]; hasNextBatch: boolean }> => {
|
|
199
|
-
const boxes = await this.api.v1.getApiV1BoxesUnspentByaddressP1(address, {
|
|
200
|
-
offset: offset,
|
|
201
|
-
limit: limit,
|
|
202
|
-
sortDirection: 'desc',
|
|
203
|
-
});
|
|
204
|
-
if (!boxes.items)
|
|
205
|
-
throw new Error('Explorer BoxesByAddress api expected to have items');
|
|
206
|
-
const resultBoxes: Array<ErgoBox> = [];
|
|
207
|
-
for (const box of boxes.items) {
|
|
208
|
-
resultBoxes.push(await this.convertBox(box));
|
|
209
|
-
}
|
|
210
|
-
return { boxes: resultBoxes, hasNextBatch: boxes.total > offset + limit };
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* use explorer api to return related boxes by specified token id
|
|
215
|
-
* @param tokenId
|
|
216
|
-
* @param offset
|
|
217
|
-
* @param limit
|
|
218
|
-
* @returns related boxes
|
|
219
|
-
*/
|
|
220
|
-
getBoxesByTokenId = async (
|
|
221
|
-
tokenId: string,
|
|
222
|
-
offset: number,
|
|
223
|
-
limit: number,
|
|
224
|
-
): Promise<{ boxes: ErgoBox[]; hasNextBatch: boolean }> => {
|
|
225
|
-
const boxes = await this.api.v1.getApiV1BoxesBytokenidP1(tokenId, {
|
|
226
|
-
offset: offset,
|
|
227
|
-
limit: limit,
|
|
228
|
-
});
|
|
229
|
-
if (!boxes.items)
|
|
230
|
-
throw new Error('Explorer BoxesByTokeId api expected to have items');
|
|
231
|
-
const resultBoxes: Array<ErgoBox> = [];
|
|
232
|
-
for (const box of boxes.items) {
|
|
233
|
-
resultBoxes.push(await this.convertBox(box));
|
|
234
|
-
}
|
|
235
|
-
return { boxes: resultBoxes, hasNextBatch: boxes.total > offset + limit };
|
|
236
|
-
};
|
|
237
|
-
}
|