@subql/node-ethereum 0.4.1-8 → 0.4.1-storeCache-0.0.3
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 +1 -0
- package/README.md +3 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/ethereum/api.ethereum.d.ts +8 -4
- package/dist/ethereum/api.ethereum.js +70 -49
- package/dist/ethereum/api.ethereum.js.map +1 -1
- package/dist/ethereum/api.service.ethereum.js +1 -1
- package/dist/ethereum/api.service.ethereum.js.map +1 -1
- package/dist/ethereum/block.ethereum.d.ts +1 -1
- package/dist/ethereum/block.ethereum.js +9 -12
- package/dist/ethereum/block.ethereum.js.map +1 -1
- package/dist/ethereum/ethers/json-rpc-batch-provider.d.ts +21 -0
- package/dist/ethereum/ethers/json-rpc-batch-provider.js +102 -0
- package/dist/ethereum/ethers/json-rpc-batch-provider.js.map +1 -0
- package/dist/ethereum/ethers/json-rpc-provider.d.ts +7 -0
- package/dist/ethereum/ethers/json-rpc-provider.js +68 -0
- package/dist/ethereum/ethers/json-rpc-provider.js.map +1 -0
- package/dist/ethereum/ethers/web/_version.d.ts +1 -0
- package/dist/ethereum/ethers/web/_version.js +6 -0
- package/dist/ethereum/ethers/web/_version.js.map +1 -0
- package/dist/ethereum/ethers/web/geturl.d.ts +3 -0
- package/dist/ethereum/ethers/web/geturl.js +116 -0
- package/dist/ethereum/ethers/web/geturl.js.map +1 -0
- package/dist/ethereum/ethers/web/index.d.ts +49 -0
- package/dist/ethereum/ethers/web/index.js +433 -0
- package/dist/ethereum/ethers/web/index.js.map +1 -0
- package/dist/ethereum/ethers/web/types.d.ts +26 -0
- package/dist/ethereum/ethers/web/types.js +4 -0
- package/dist/ethereum/ethers/web/types.js.map +1 -0
- package/dist/ethereum/utils.ethereum.d.ts +1 -1
- package/dist/ethereum/utils.ethereum.js +16 -3
- package/dist/ethereum/utils.ethereum.js.map +1 -1
- package/dist/indexer/blockDispatcher/block-dispatcher.service.d.ts +8 -15
- package/dist/indexer/blockDispatcher/block-dispatcher.service.js +20 -108
- package/dist/indexer/blockDispatcher/block-dispatcher.service.js.map +1 -1
- package/dist/indexer/blockDispatcher/ethereum-block-dispatcher.d.ts +4 -0
- package/dist/indexer/blockDispatcher/ethereum-block-dispatcher.js +5 -0
- package/dist/indexer/blockDispatcher/ethereum-block-dispatcher.js.map +1 -0
- package/dist/indexer/blockDispatcher/index.d.ts +2 -2
- package/dist/indexer/blockDispatcher/index.js.map +1 -1
- package/dist/indexer/blockDispatcher/worker-block-dispatcher.service.d.ts +12 -17
- package/dist/indexer/blockDispatcher/worker-block-dispatcher.service.js +48 -134
- package/dist/indexer/blockDispatcher/worker-block-dispatcher.service.js.map +1 -1
- package/dist/indexer/dynamic-ds.service.d.ts +3 -22
- package/dist/indexer/dynamic-ds.service.js +4 -91
- package/dist/indexer/dynamic-ds.service.js.map +1 -1
- package/dist/indexer/fetch.module.js +24 -6
- package/dist/indexer/fetch.module.js.map +1 -1
- package/dist/indexer/fetch.service.d.ts +3 -3
- package/dist/indexer/fetch.service.js +40 -39
- package/dist/indexer/fetch.service.js.map +1 -1
- package/dist/indexer/indexer.manager.d.ts +5 -17
- package/dist/indexer/indexer.manager.js +25 -70
- package/dist/indexer/indexer.manager.js.map +1 -1
- package/dist/indexer/indexer.module.js +25 -2
- package/dist/indexer/indexer.module.js.map +1 -1
- package/dist/indexer/project.service.d.ts +5 -10
- package/dist/indexer/project.service.js +49 -100
- package/dist/indexer/project.service.js.map +1 -1
- package/dist/indexer/sandbox.service.js +6 -1
- package/dist/indexer/sandbox.service.js.map +1 -1
- package/dist/indexer/unfinalizedBlocks.service.d.ts +12 -12
- package/dist/indexer/unfinalizedBlocks.service.js +33 -38
- package/dist/indexer/unfinalizedBlocks.service.js.map +1 -1
- package/dist/indexer/unfinalizedBlocks.spec.js +41 -34
- package/dist/indexer/unfinalizedBlocks.spec.js.map +1 -1
- package/dist/indexer/worker/worker.d.ts +22 -8
- package/dist/indexer/worker/worker.js +14 -7
- package/dist/indexer/worker/worker.js.map +1 -1
- package/dist/indexer/worker/worker.service.d.ts +2 -2
- package/dist/indexer/worker/worker.service.js +11 -4
- package/dist/indexer/worker/worker.service.js.map +1 -1
- package/dist/indexer/worker/worker.unfinalizedBlocks.service.d.ts +11 -0
- package/dist/indexer/worker/worker.unfinalizedBlocks.service.js +32 -0
- package/dist/indexer/worker/worker.unfinalizedBlocks.service.js.map +1 -0
- package/dist/init.js +2 -2
- package/dist/init.js.map +1 -1
- package/dist/meta/meta.module.js +8 -0
- package/dist/meta/meta.module.js.map +1 -1
- package/dist/meta/meta.service.d.ts +18 -3
- package/dist/meta/meta.service.js +89 -5
- package/dist/meta/meta.service.js.map +1 -1
- package/dist/subcommands/forceClean.service.js +8 -4
- package/dist/subcommands/forceClean.service.js.map +1 -1
- package/dist/subcommands/reindex.init.js +5 -1
- package/dist/subcommands/reindex.init.js.map +1 -1
- package/dist/subcommands/reindex.module.js +8 -0
- package/dist/subcommands/reindex.module.js.map +1 -1
- package/dist/subcommands/reindex.service.d.ts +4 -1
- package/dist/subcommands/reindex.service.js +21 -10
- package/dist/subcommands/reindex.service.js.map +1 -1
- package/dist/utils/project.d.ts +1 -0
- package/dist/utils/project.js +13 -1
- package/dist/utils/project.js.map +1 -1
- package/dist/utils/reindex.d.ts +2 -1
- package/dist/utils/reindex.js +6 -2
- package/dist/utils/reindex.js.map +1 -1
- package/dist/utils/string.js +10 -2
- package/dist/utils/string.js.map +1 -1
- package/dist/yargs.d.ts +85 -53
- package/dist/yargs.js +103 -71
- package/dist/yargs.js.map +1 -1
- package/package.json +9 -9
- package/dist/indexer/blockDispatcher/base-block-dispatcher.d.ts +0 -40
- package/dist/indexer/blockDispatcher/base-block-dispatcher.js +0 -99
- package/dist/indexer/blockDispatcher/base-block-dispatcher.js.map +0 -1
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
import { Block } from '@ethersproject/abstract-provider';
|
|
2
|
-
import { ApiService,
|
|
2
|
+
import { ApiService, NodeConfig, StoreCacheService } from '@subql/node-core';
|
|
3
3
|
import { EthereumBlock } from '@subql/types-ethereum';
|
|
4
|
-
import { Sequelize, Transaction } from 'sequelize';
|
|
5
4
|
export declare const METADATA_UNFINALIZED_BLOCKS_KEY = "unfinalizedBlocks";
|
|
6
5
|
export declare const METADATA_LAST_FINALIZED_PROCESSED_KEY = "lastFinalizedVerifiedHeight";
|
|
7
6
|
declare type UnfinalizedBlocks = [blockHeight: number, blockHash: string][];
|
|
8
|
-
export
|
|
7
|
+
export interface IUnfinalizedBlocksService {
|
|
8
|
+
processUnfinalizedBlocks(block: EthereumBlock | undefined): Promise<number | null>;
|
|
9
|
+
}
|
|
10
|
+
export declare class UnfinalizedBlocksService implements IUnfinalizedBlocksService {
|
|
9
11
|
private readonly apiService;
|
|
10
12
|
private readonly nodeConfig;
|
|
11
|
-
private readonly
|
|
13
|
+
private readonly storeCache;
|
|
12
14
|
private unfinalizedBlocks;
|
|
13
15
|
private finalizedHeader;
|
|
14
|
-
private metadataRepo;
|
|
15
16
|
private lastCheckedBlockHeight;
|
|
16
|
-
constructor(apiService: ApiService, nodeConfig: NodeConfig,
|
|
17
|
-
init(
|
|
18
|
-
processUnfinalizedBlocks(block: EthereumBlock | undefined
|
|
17
|
+
constructor(apiService: ApiService, nodeConfig: NodeConfig, storeCache: StoreCacheService);
|
|
18
|
+
init(reindex: (targetHeight: number) => Promise<void>): Promise<number | undefined>;
|
|
19
|
+
processUnfinalizedBlocks(block: EthereumBlock | undefined): Promise<number | null>;
|
|
19
20
|
private get api();
|
|
20
21
|
private get finalizedBlockNumber();
|
|
21
22
|
registerFinalizedBlock(header: Block): void;
|
|
@@ -24,12 +25,11 @@ export declare class UnfinalizedBlocksService {
|
|
|
24
25
|
private removeFinalized;
|
|
25
26
|
private getClosestRecord;
|
|
26
27
|
private hasForked;
|
|
27
|
-
private saveUnfinalizedBlocks;
|
|
28
28
|
private getLastCorrectFinalizedBlock;
|
|
29
|
-
|
|
30
|
-
resetLastFinalizedVerifiedHeight(tx: Transaction): Promise<void>;
|
|
31
|
-
private setMetadata;
|
|
29
|
+
private saveUnfinalizedBlocks;
|
|
32
30
|
private saveLastFinalizedVerifiedHeight;
|
|
31
|
+
resetUnfinalizedBlocks(): void;
|
|
32
|
+
resetLastFinalizedVerifiedHeight(): void;
|
|
33
33
|
getMetadataUnfinalizedBlocks(): Promise<UnfinalizedBlocks>;
|
|
34
34
|
getLastFinalizedVerifiedHeight(): Promise<number | undefined>;
|
|
35
35
|
}
|
|
@@ -10,34 +10,32 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
10
10
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
11
11
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
12
12
|
};
|
|
13
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
14
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
15
|
-
};
|
|
16
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
14
|
exports.UnfinalizedBlocksService = exports.METADATA_LAST_FINALIZED_PROCESSED_KEY = exports.METADATA_UNFINALIZED_BLOCKS_KEY = void 0;
|
|
18
|
-
const assert_1 = __importDefault(require("assert"));
|
|
19
15
|
const common_1 = require("@nestjs/common");
|
|
20
16
|
const node_core_1 = require("@subql/node-core");
|
|
21
17
|
const lodash_1 = require("lodash");
|
|
22
|
-
|
|
18
|
+
// import { ApiService } from './api.service';
|
|
23
19
|
const logger = (0, node_core_1.getLogger)('UnfinalizedBlocks');
|
|
24
20
|
exports.METADATA_UNFINALIZED_BLOCKS_KEY = 'unfinalizedBlocks';
|
|
25
21
|
exports.METADATA_LAST_FINALIZED_PROCESSED_KEY = 'lastFinalizedVerifiedHeight';
|
|
26
22
|
const UNFINALIZED_THRESHOLD = 200;
|
|
27
23
|
let UnfinalizedBlocksService = class UnfinalizedBlocksService {
|
|
28
|
-
constructor(apiService, nodeConfig,
|
|
24
|
+
constructor(apiService, nodeConfig, storeCache) {
|
|
29
25
|
this.apiService = apiService;
|
|
30
26
|
this.nodeConfig = nodeConfig;
|
|
31
|
-
this.
|
|
27
|
+
this.storeCache = storeCache;
|
|
32
28
|
}
|
|
33
|
-
async init(
|
|
34
|
-
this.
|
|
29
|
+
async init(reindex) {
|
|
30
|
+
logger.info(`Unfinalized blocks is ${this.nodeConfig.unfinalizedBlocks ? 'enabled' : 'disabled'}`);
|
|
31
|
+
// unfinalized blocks
|
|
35
32
|
this.unfinalizedBlocks = await this.getMetadataUnfinalizedBlocks();
|
|
36
33
|
this.lastCheckedBlockHeight = await this.getLastFinalizedVerifiedHeight();
|
|
37
34
|
this.finalizedHeader = await this.api.getBlockByHeightOrHash(await this.api.getFinalizedBlockHeight());
|
|
38
35
|
if (!this.nodeConfig.unfinalizedBlocks && this.unfinalizedBlocks.length) {
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
logger.info('Processing unfinalized blocks');
|
|
37
|
+
// Validate any previously unfinalized blocks
|
|
38
|
+
const rewindHeight = await this.processUnfinalizedBlocks(null);
|
|
41
39
|
if (rewindHeight !== null) {
|
|
42
40
|
logger.info(`Found un-finalized blocks from previous indexing but unverified, rolling back to last finalized block ${rewindHeight} `);
|
|
43
41
|
await reindex(rewindHeight);
|
|
@@ -45,20 +43,19 @@ let UnfinalizedBlocksService = class UnfinalizedBlocksService {
|
|
|
45
43
|
return rewindHeight;
|
|
46
44
|
}
|
|
47
45
|
else {
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
this.resetUnfinalizedBlocks();
|
|
47
|
+
this.resetLastFinalizedVerifiedHeight();
|
|
50
48
|
}
|
|
51
|
-
await tx.commit();
|
|
52
49
|
}
|
|
53
50
|
}
|
|
54
|
-
async processUnfinalizedBlocks(block
|
|
51
|
+
async processUnfinalizedBlocks(block) {
|
|
55
52
|
if (block) {
|
|
56
|
-
|
|
53
|
+
this.registerUnfinalizedBlock(block.number, block.hash);
|
|
57
54
|
}
|
|
58
55
|
const forkedHeader = await this.hasForked();
|
|
59
56
|
if (!forkedHeader) {
|
|
60
57
|
// Remove blocks that are now confirmed finalized
|
|
61
|
-
|
|
58
|
+
this.deleteFinalizedBlock();
|
|
62
59
|
}
|
|
63
60
|
else {
|
|
64
61
|
// Get the last unfinalized block that is now finalized
|
|
@@ -78,7 +75,7 @@ let UnfinalizedBlocksService = class UnfinalizedBlocksService {
|
|
|
78
75
|
}
|
|
79
76
|
this.finalizedHeader = header;
|
|
80
77
|
}
|
|
81
|
-
|
|
78
|
+
registerUnfinalizedBlock(blockNumber, hash) {
|
|
82
79
|
if (blockNumber <= this.finalizedBlockNumber)
|
|
83
80
|
return;
|
|
84
81
|
// Ensure order
|
|
@@ -88,14 +85,14 @@ let UnfinalizedBlocksService = class UnfinalizedBlocksService {
|
|
|
88
85
|
process.exit(1);
|
|
89
86
|
}
|
|
90
87
|
this.unfinalizedBlocks.push([blockNumber, hash]);
|
|
91
|
-
|
|
88
|
+
this.saveUnfinalizedBlocks(this.unfinalizedBlocks);
|
|
92
89
|
}
|
|
93
|
-
|
|
90
|
+
deleteFinalizedBlock() {
|
|
94
91
|
if (this.lastCheckedBlockHeight !== undefined &&
|
|
95
92
|
this.lastCheckedBlockHeight < this.finalizedBlockNumber) {
|
|
96
93
|
this.removeFinalized(this.finalizedBlockNumber);
|
|
97
|
-
|
|
98
|
-
|
|
94
|
+
this.saveLastFinalizedVerifiedHeight(this.finalizedBlockNumber);
|
|
95
|
+
this.saveUnfinalizedBlocks(this.unfinalizedBlocks);
|
|
99
96
|
}
|
|
100
97
|
this.lastCheckedBlockHeight = this.finalizedBlockNumber;
|
|
101
98
|
}
|
|
@@ -115,6 +112,7 @@ let UnfinalizedBlocksService = class UnfinalizedBlocksService {
|
|
|
115
112
|
}
|
|
116
113
|
return undefined;
|
|
117
114
|
}
|
|
115
|
+
// check unfinalized blocks for a fork, returns the header where a fork happened
|
|
118
116
|
async hasForked() {
|
|
119
117
|
const lastVerifiableBlock = this.getClosestRecord(this.finalizedBlockNumber);
|
|
120
118
|
// No unfinalized blocks
|
|
@@ -152,9 +150,6 @@ let UnfinalizedBlocksService = class UnfinalizedBlocksService {
|
|
|
152
150
|
}
|
|
153
151
|
return;
|
|
154
152
|
}
|
|
155
|
-
async saveUnfinalizedBlocks(unfinalizedBlocks, tx) {
|
|
156
|
-
return this.setMetadata(exports.METADATA_UNFINALIZED_BLOCKS_KEY, JSON.stringify(unfinalizedBlocks), tx);
|
|
157
|
-
}
|
|
158
153
|
async getLastCorrectFinalizedBlock(forkedHeader) {
|
|
159
154
|
const bestVerifiableBlocks = this.unfinalizedBlocks.filter(([bestBlockHeight]) => Number(bestBlockHeight) <= this.finalizedBlockNumber);
|
|
160
155
|
let checkingHeader = forkedHeader;
|
|
@@ -168,36 +163,36 @@ let UnfinalizedBlocksService = class UnfinalizedBlocksService {
|
|
|
168
163
|
}
|
|
169
164
|
return this.lastCheckedBlockHeight;
|
|
170
165
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
this.unfinalizedBlocks = [];
|
|
166
|
+
saveUnfinalizedBlocks(unfinalizedBlocks) {
|
|
167
|
+
return this.storeCache.metadata.set(exports.METADATA_UNFINALIZED_BLOCKS_KEY, JSON.stringify(unfinalizedBlocks));
|
|
174
168
|
}
|
|
175
|
-
|
|
176
|
-
return this.
|
|
169
|
+
saveLastFinalizedVerifiedHeight(height) {
|
|
170
|
+
return this.storeCache.metadata.set(exports.METADATA_LAST_FINALIZED_PROCESSED_KEY, height);
|
|
177
171
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
172
|
+
resetUnfinalizedBlocks() {
|
|
173
|
+
this.storeCache.metadata.set(exports.METADATA_UNFINALIZED_BLOCKS_KEY, '[]');
|
|
174
|
+
this.unfinalizedBlocks = [];
|
|
181
175
|
}
|
|
182
|
-
|
|
183
|
-
return this.
|
|
176
|
+
resetLastFinalizedVerifiedHeight() {
|
|
177
|
+
return this.storeCache.metadata.set(exports.METADATA_LAST_FINALIZED_PROCESSED_KEY, null);
|
|
184
178
|
}
|
|
179
|
+
//string should be jsonb object
|
|
185
180
|
async getMetadataUnfinalizedBlocks() {
|
|
186
|
-
const val = await
|
|
181
|
+
const val = await this.storeCache.metadata.find(exports.METADATA_UNFINALIZED_BLOCKS_KEY);
|
|
187
182
|
if (val) {
|
|
188
183
|
return JSON.parse(val);
|
|
189
184
|
}
|
|
190
185
|
return [];
|
|
191
186
|
}
|
|
192
187
|
async getLastFinalizedVerifiedHeight() {
|
|
193
|
-
return
|
|
188
|
+
return this.storeCache.metadata.find(exports.METADATA_LAST_FINALIZED_PROCESSED_KEY);
|
|
194
189
|
}
|
|
195
190
|
};
|
|
196
191
|
UnfinalizedBlocksService = __decorate([
|
|
197
192
|
(0, common_1.Injectable)(),
|
|
198
193
|
__metadata("design:paramtypes", [node_core_1.ApiService,
|
|
199
194
|
node_core_1.NodeConfig,
|
|
200
|
-
|
|
195
|
+
node_core_1.StoreCacheService])
|
|
201
196
|
], UnfinalizedBlocksService);
|
|
202
197
|
exports.UnfinalizedBlocksService = UnfinalizedBlocksService;
|
|
203
198
|
//# sourceMappingURL=unfinalizedBlocks.service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unfinalizedBlocks.service.js","sourceRoot":"","sources":["../../src/indexer/unfinalizedBlocks.service.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2020-2022 OnFinality Limited authors & contributors\n// SPDX-License-Identifier: Apache-2.0\n\nimport assert from 'assert';\nimport { Block } from '@ethersproject/abstract-provider';\nimport { Injectable } from '@nestjs/common';\nimport {\n ApiService,\n getLogger,\n getMetaDataInfo,\n Metadata,\n MetadataRepo,\n NodeConfig,\n} from '@subql/node-core';\nimport { EthereumBlock } from '@subql/types-ethereum';\nimport { last } from 'lodash';\nimport { Sequelize, Transaction } from 'sequelize';\nimport { EthereumApi } from '../ethereum';\n\nconst logger = getLogger('UnfinalizedBlocks');\n\nexport const METADATA_UNFINALIZED_BLOCKS_KEY = 'unfinalizedBlocks';\nexport const METADATA_LAST_FINALIZED_PROCESSED_KEY =\n 'lastFinalizedVerifiedHeight';\n\nconst UNFINALIZED_THRESHOLD = 200;\n\ntype UnfinalizedBlocks = [blockHeight: number, blockHash: string][];\n\n@Injectable()\nexport class UnfinalizedBlocksService {\n private unfinalizedBlocks: UnfinalizedBlocks;\n private finalizedHeader: Block;\n private metadataRepo: MetadataRepo;\n private lastCheckedBlockHeight: number;\n\n constructor(\n private readonly apiService: ApiService,\n private readonly nodeConfig: NodeConfig,\n private readonly sequelize: Sequelize,\n ) {}\n\n async init(\n metadataRepo: MetadataRepo,\n reindex: (targetHeight: number) => Promise<void>,\n ): Promise<number | undefined> {\n this.metadataRepo = metadataRepo;\n this.unfinalizedBlocks = await this.getMetadataUnfinalizedBlocks();\n this.lastCheckedBlockHeight = await this.getLastFinalizedVerifiedHeight();\n this.finalizedHeader = await this.api.getBlockByHeightOrHash(\n await this.api.getFinalizedBlockHeight(),\n );\n\n if (!this.nodeConfig.unfinalizedBlocks && this.unfinalizedBlocks.length) {\n const tx = await this.sequelize.transaction();\n const rewindHeight = await this.processUnfinalizedBlocks(null, tx);\n\n if (rewindHeight !== null) {\n logger.info(\n `Found un-finalized blocks from previous indexing but unverified, rolling back to last finalized block ${rewindHeight} `,\n );\n await reindex(rewindHeight);\n logger.info(`Successful rewind to block ${rewindHeight}!`);\n return rewindHeight;\n } else {\n await this.resetUnfinalizedBlocks(tx);\n await this.resetLastFinalizedVerifiedHeight(tx);\n }\n await tx.commit();\n }\n }\n\n async processUnfinalizedBlocks(\n block: EthereumBlock | undefined,\n tx: Transaction,\n ): Promise<number | null> {\n if (block) {\n await this.registerUnfinalizedBlock(block.number, block.hash, tx);\n }\n\n const forkedHeader = await this.hasForked();\n if (!forkedHeader) {\n // Remove blocks that are now confirmed finalized\n await this.deleteFinalizedBlock(tx);\n } else {\n // Get the last unfinalized block that is now finalized\n return this.getLastCorrectFinalizedBlock(forkedHeader);\n }\n\n return null;\n }\n\n private get api(): EthereumApi {\n return this.apiService.api;\n }\n\n private get finalizedBlockNumber(): number {\n return this.finalizedHeader.number;\n }\n\n registerFinalizedBlock(header: Block): void {\n if (this.finalizedHeader && this.finalizedBlockNumber >= header.number) {\n return;\n }\n this.finalizedHeader = header;\n }\n\n private async registerUnfinalizedBlock(\n blockNumber: number,\n hash: string,\n tx: Transaction,\n ): Promise<void> {\n if (blockNumber <= this.finalizedBlockNumber) return;\n\n // Ensure order\n if (\n this.unfinalizedBlocks.length &&\n last(this.unfinalizedBlocks)[0] + 1 !== blockNumber\n ) {\n logger.error('Unfinalized block is not sequential');\n process.exit(1);\n }\n\n this.unfinalizedBlocks.push([blockNumber, hash]);\n await this.saveUnfinalizedBlocks(this.unfinalizedBlocks, tx);\n }\n\n private async deleteFinalizedBlock(tx: Transaction): Promise<void> {\n if (\n this.lastCheckedBlockHeight !== undefined &&\n this.lastCheckedBlockHeight < this.finalizedBlockNumber\n ) {\n this.removeFinalized(this.finalizedBlockNumber);\n await this.saveLastFinalizedVerifiedHeight(this.finalizedBlockNumber, tx);\n await this.saveUnfinalizedBlocks(this.unfinalizedBlocks, tx);\n }\n this.lastCheckedBlockHeight = this.finalizedBlockNumber;\n }\n\n // remove any records less and equal than input finalized blockHeight\n private removeFinalized(blockHeight: number): void {\n this.unfinalizedBlocks = this.unfinalizedBlocks.filter(\n ([height]) => height > blockHeight,\n );\n }\n\n // find closest record from block heights\n private getClosestRecord(\n blockHeight: number,\n ): { blockHeight: number; hash: string } | undefined {\n // Have the block in the best block, can be verified\n const record = [...this.unfinalizedBlocks] // Copy so we can reverse\n .reverse() // Reverse the list to find the largest block\n .find(([bestBlockHeight]) => Number(bestBlockHeight) <= blockHeight);\n if (record) {\n const [bestBlockHeight, hash] = record;\n return { blockHeight: Number(bestBlockHeight), hash };\n }\n return undefined;\n }\n\n private async hasForked(): Promise<Block | undefined> {\n const lastVerifiableBlock = this.getClosestRecord(\n this.finalizedBlockNumber,\n );\n\n // No unfinalized blocks\n if (!lastVerifiableBlock) {\n return;\n }\n\n // Unfinalized blocks beyond finalized block\n if (lastVerifiableBlock.blockHeight === this.finalizedBlockNumber) {\n if (lastVerifiableBlock.hash !== this.finalizedHeader.hash) {\n logger.warn(\n `Block fork found, enqueued un-finalized block at ${lastVerifiableBlock.blockHeight} with hash ${lastVerifiableBlock.hash}, actual hash is ${this.finalizedHeader.hash}`,\n );\n return this.finalizedHeader;\n }\n } else {\n // Unfinalized blocks below finalized block\n let header = this.finalizedHeader;\n /*\n * Iterate back through parent hashes until we get the header with the matching height\n * We use headers here rather than getBlockHash because of potential caching issues on the rpc\n * If we're off by a large number of blocks we can optimise by getting the block hash directly\n */\n if (\n header.number - lastVerifiableBlock.blockHeight >\n UNFINALIZED_THRESHOLD\n ) {\n header = await this.api.getBlockByHeightOrHash(\n lastVerifiableBlock.blockHeight,\n );\n } else {\n while (lastVerifiableBlock.blockHeight !== header.number) {\n header = await this.api.getBlockByHeightOrHash(header.parentHash);\n }\n }\n\n if (header.hash !== lastVerifiableBlock.hash) {\n logger.warn(\n `Block fork found, enqueued un-finalized block at ${lastVerifiableBlock.blockHeight} with hash ${lastVerifiableBlock.hash}, actual hash is ${header.hash}`,\n );\n return header;\n }\n }\n\n return;\n }\n\n private async saveUnfinalizedBlocks(\n unfinalizedBlocks: UnfinalizedBlocks,\n tx: Transaction,\n ): Promise<void> {\n return this.setMetadata(\n METADATA_UNFINALIZED_BLOCKS_KEY,\n JSON.stringify(unfinalizedBlocks),\n tx,\n );\n }\n\n private async getLastCorrectFinalizedBlock(\n forkedHeader: Block,\n ): Promise<number | undefined> {\n const bestVerifiableBlocks = this.unfinalizedBlocks.filter(\n ([bestBlockHeight]) =>\n Number(bestBlockHeight) <= this.finalizedBlockNumber,\n );\n\n let checkingHeader = forkedHeader;\n\n // Work backwards through the blocks until we find a matching hash\n for (const [block, hash] of bestVerifiableBlocks.reverse()) {\n if (hash === checkingHeader.hash || hash === checkingHeader.parentHash) {\n return Number(block);\n }\n\n // Get the new parent\n checkingHeader = await this.api.getBlockByHeightOrHash(\n checkingHeader.parentHash,\n );\n }\n\n return this.lastCheckedBlockHeight;\n }\n\n async resetUnfinalizedBlocks(tx: Transaction): Promise<void> {\n await this.setMetadata(METADATA_UNFINALIZED_BLOCKS_KEY, '[]', tx);\n this.unfinalizedBlocks = [];\n }\n\n async resetLastFinalizedVerifiedHeight(tx: Transaction): Promise<void> {\n return this.setMetadata(METADATA_LAST_FINALIZED_PROCESSED_KEY, null, tx);\n }\n\n private async setMetadata(\n key: Metadata['key'],\n value: Metadata['value'],\n tx: Transaction,\n ): Promise<void> {\n assert(this.metadataRepo, `Model _metadata does not exist`);\n await this.metadataRepo.upsert({ key, value }, { transaction: tx });\n }\n\n private async saveLastFinalizedVerifiedHeight(\n height: number,\n tx: Transaction,\n ) {\n return this.setMetadata(METADATA_LAST_FINALIZED_PROCESSED_KEY, height, tx);\n }\n\n async getMetadataUnfinalizedBlocks(): Promise<UnfinalizedBlocks> {\n const val = await getMetaDataInfo<string>(\n this.metadataRepo,\n METADATA_UNFINALIZED_BLOCKS_KEY,\n );\n if (val) {\n return JSON.parse(val) as UnfinalizedBlocks;\n }\n return [];\n }\n\n async getLastFinalizedVerifiedHeight(): Promise<number | undefined> {\n return getMetaDataInfo(\n this.metadataRepo,\n METADATA_LAST_FINALIZED_PROCESSED_KEY,\n );\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"unfinalizedBlocks.service.js","sourceRoot":"","sources":["../../src/indexer/unfinalizedBlocks.service.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2020-2022 OnFinality Limited authors & contributors\n// SPDX-License-Identifier: Apache-2.0\n\nimport { Block } from '@ethersproject/abstract-provider';\nimport { Injectable } from '@nestjs/common';\nimport {\n ApiService,\n getLogger,\n NodeConfig,\n StoreCacheService,\n} from '@subql/node-core';\nimport { EthereumBlock } from '@subql/types-ethereum';\nimport { last } from 'lodash';\nimport { EthereumApi } from '../ethereum';\n// import { ApiService } from './api.service';\n\nconst logger = getLogger('UnfinalizedBlocks');\n\nexport const METADATA_UNFINALIZED_BLOCKS_KEY = 'unfinalizedBlocks';\nexport const METADATA_LAST_FINALIZED_PROCESSED_KEY =\n 'lastFinalizedVerifiedHeight';\n\nconst UNFINALIZED_THRESHOLD = 200;\n\ntype UnfinalizedBlocks = [blockHeight: number, blockHash: string][];\nexport interface IUnfinalizedBlocksService {\n processUnfinalizedBlocks(\n block: EthereumBlock | undefined,\n ): Promise<number | null>;\n}\n\n@Injectable()\nexport class UnfinalizedBlocksService implements IUnfinalizedBlocksService {\n private unfinalizedBlocks: UnfinalizedBlocks;\n private finalizedHeader: Block;\n private lastCheckedBlockHeight: number;\n\n constructor(\n private readonly apiService: ApiService,\n private readonly nodeConfig: NodeConfig,\n private readonly storeCache: StoreCacheService,\n ) {}\n\n async init(\n reindex: (targetHeight: number) => Promise<void>,\n ): Promise<number | undefined> {\n logger.info(\n `Unfinalized blocks is ${\n this.nodeConfig.unfinalizedBlocks ? 'enabled' : 'disabled'\n }`,\n );\n // unfinalized blocks\n this.unfinalizedBlocks = await this.getMetadataUnfinalizedBlocks();\n this.lastCheckedBlockHeight = await this.getLastFinalizedVerifiedHeight();\n this.finalizedHeader = await this.api.getBlockByHeightOrHash(\n await this.api.getFinalizedBlockHeight(),\n );\n\n if (!this.nodeConfig.unfinalizedBlocks && this.unfinalizedBlocks.length) {\n logger.info('Processing unfinalized blocks');\n // Validate any previously unfinalized blocks\n\n const rewindHeight = await this.processUnfinalizedBlocks(null);\n\n if (rewindHeight !== null) {\n logger.info(\n `Found un-finalized blocks from previous indexing but unverified, rolling back to last finalized block ${rewindHeight} `,\n );\n await reindex(rewindHeight);\n logger.info(`Successful rewind to block ${rewindHeight}!`);\n return rewindHeight;\n } else {\n this.resetUnfinalizedBlocks();\n this.resetLastFinalizedVerifiedHeight();\n }\n }\n }\n\n async processUnfinalizedBlocks(\n block: EthereumBlock | undefined,\n ): Promise<number | null> {\n if (block) {\n this.registerUnfinalizedBlock(block.number, block.hash);\n }\n\n const forkedHeader = await this.hasForked();\n if (!forkedHeader) {\n // Remove blocks that are now confirmed finalized\n this.deleteFinalizedBlock();\n } else {\n // Get the last unfinalized block that is now finalized\n return this.getLastCorrectFinalizedBlock(forkedHeader);\n }\n\n return null;\n }\n\n private get api(): EthereumApi {\n return this.apiService.api;\n }\n\n private get finalizedBlockNumber(): number {\n return this.finalizedHeader.number;\n }\n\n registerFinalizedBlock(header: Block): void {\n if (this.finalizedHeader && this.finalizedBlockNumber >= header.number) {\n return;\n }\n this.finalizedHeader = header;\n }\n\n private registerUnfinalizedBlock(blockNumber: number, hash: string): void {\n if (blockNumber <= this.finalizedBlockNumber) return;\n\n // Ensure order\n if (\n this.unfinalizedBlocks.length &&\n last(this.unfinalizedBlocks)[0] + 1 !== blockNumber\n ) {\n logger.error('Unfinalized block is not sequential');\n process.exit(1);\n }\n\n this.unfinalizedBlocks.push([blockNumber, hash]);\n\n this.saveUnfinalizedBlocks(this.unfinalizedBlocks);\n }\n\n private deleteFinalizedBlock(): void {\n if (\n this.lastCheckedBlockHeight !== undefined &&\n this.lastCheckedBlockHeight < this.finalizedBlockNumber\n ) {\n this.removeFinalized(this.finalizedBlockNumber);\n this.saveLastFinalizedVerifiedHeight(this.finalizedBlockNumber);\n this.saveUnfinalizedBlocks(this.unfinalizedBlocks);\n }\n this.lastCheckedBlockHeight = this.finalizedBlockNumber;\n }\n\n // remove any records less and equal than input finalized blockHeight\n private removeFinalized(blockHeight: number): void {\n this.unfinalizedBlocks = this.unfinalizedBlocks.filter(\n ([height]) => height > blockHeight,\n );\n }\n\n // find closest record from block heights\n private getClosestRecord(\n blockHeight: number,\n ): { blockHeight: number; hash: string } | undefined {\n // Have the block in the best block, can be verified\n const record = [...this.unfinalizedBlocks] // Copy so we can reverse\n .reverse() // Reverse the list to find the largest block\n .find(([bestBlockHeight]) => Number(bestBlockHeight) <= blockHeight);\n if (record) {\n const [bestBlockHeight, hash] = record;\n return { blockHeight: Number(bestBlockHeight), hash };\n }\n return undefined;\n }\n\n // check unfinalized blocks for a fork, returns the header where a fork happened\n private async hasForked(): Promise<Block | undefined> {\n const lastVerifiableBlock = this.getClosestRecord(\n this.finalizedBlockNumber,\n );\n\n // No unfinalized blocks\n if (!lastVerifiableBlock) {\n return;\n }\n\n // Unfinalized blocks beyond finalized block\n if (lastVerifiableBlock.blockHeight === this.finalizedBlockNumber) {\n if (lastVerifiableBlock.hash !== this.finalizedHeader.hash) {\n logger.warn(\n `Block fork found, enqueued un-finalized block at ${lastVerifiableBlock.blockHeight} with hash ${lastVerifiableBlock.hash}, actual hash is ${this.finalizedHeader.hash}`,\n );\n return this.finalizedHeader;\n }\n } else {\n // Unfinalized blocks below finalized block\n let header = this.finalizedHeader;\n /*\n * Iterate back through parent hashes until we get the header with the matching height\n * We use headers here rather than getBlockHash because of potential caching issues on the rpc\n * If we're off by a large number of blocks we can optimise by getting the block hash directly\n */\n if (\n header.number - lastVerifiableBlock.blockHeight >\n UNFINALIZED_THRESHOLD\n ) {\n header = await this.api.getBlockByHeightOrHash(\n lastVerifiableBlock.blockHeight,\n );\n } else {\n while (lastVerifiableBlock.blockHeight !== header.number) {\n header = await this.api.getBlockByHeightOrHash(header.parentHash);\n }\n }\n\n if (header.hash !== lastVerifiableBlock.hash) {\n logger.warn(\n `Block fork found, enqueued un-finalized block at ${lastVerifiableBlock.blockHeight} with hash ${lastVerifiableBlock.hash}, actual hash is ${header.hash}`,\n );\n return header;\n }\n }\n\n return;\n }\n\n private async getLastCorrectFinalizedBlock(\n forkedHeader: Block,\n ): Promise<number | undefined> {\n const bestVerifiableBlocks = this.unfinalizedBlocks.filter(\n ([bestBlockHeight]) =>\n Number(bestBlockHeight) <= this.finalizedBlockNumber,\n );\n\n let checkingHeader = forkedHeader;\n\n // Work backwards through the blocks until we find a matching hash\n for (const [block, hash] of bestVerifiableBlocks.reverse()) {\n if (hash === checkingHeader.hash || hash === checkingHeader.parentHash) {\n return Number(block);\n }\n\n // Get the new parent\n checkingHeader = await this.api.getBlockByHeightOrHash(\n checkingHeader.parentHash,\n );\n }\n\n return this.lastCheckedBlockHeight;\n }\n\n private saveUnfinalizedBlocks(unfinalizedBlocks: UnfinalizedBlocks): void {\n return this.storeCache.metadata.set(\n METADATA_UNFINALIZED_BLOCKS_KEY,\n JSON.stringify(unfinalizedBlocks),\n );\n }\n\n private saveLastFinalizedVerifiedHeight(height: number): void {\n return this.storeCache.metadata.set(\n METADATA_LAST_FINALIZED_PROCESSED_KEY,\n height,\n );\n }\n\n resetUnfinalizedBlocks(): void {\n this.storeCache.metadata.set(METADATA_UNFINALIZED_BLOCKS_KEY, '[]');\n this.unfinalizedBlocks = [];\n }\n\n resetLastFinalizedVerifiedHeight(): void {\n return this.storeCache.metadata.set(\n METADATA_LAST_FINALIZED_PROCESSED_KEY,\n null,\n );\n }\n\n //string should be jsonb object\n async getMetadataUnfinalizedBlocks(): Promise<UnfinalizedBlocks> {\n const val = await this.storeCache.metadata.find(\n METADATA_UNFINALIZED_BLOCKS_KEY,\n );\n if (val) {\n return JSON.parse(val) as UnfinalizedBlocks;\n }\n return [];\n }\n\n async getLastFinalizedVerifiedHeight(): Promise<number | undefined> {\n return this.storeCache.metadata.find(METADATA_LAST_FINALIZED_PROCESSED_KEY);\n }\n}\n"]}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Copyright 2020-2022 OnFinality Limited authors & contributors
|
|
3
3
|
// SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
const node_core_1 = require("@subql/node-core");
|
|
5
6
|
const unfinalizedBlocks_service_1 = require("./unfinalizedBlocks.service");
|
|
6
7
|
/* Notes:
|
|
7
8
|
* Block hashes all have the format '0xabc' + block number
|
|
@@ -39,6 +40,12 @@ function getMockMetadata() {
|
|
|
39
40
|
return {
|
|
40
41
|
upsert: ({ key, value }) => (data[key] = value),
|
|
41
42
|
findOne: ({ where: { key } }) => ({ value: data[key] }),
|
|
43
|
+
findByPk: (key) => data[key],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function mockStoreCache() {
|
|
47
|
+
return {
|
|
48
|
+
metadata: new node_core_1.CacheMetadataModel(getMockMetadata()),
|
|
42
49
|
};
|
|
43
50
|
}
|
|
44
51
|
function mockBlock(height, hash, parentHash) {
|
|
@@ -51,10 +58,10 @@ function mockBlock(height, hash, parentHash) {
|
|
|
51
58
|
describe('UnfinalizedBlocksService', () => {
|
|
52
59
|
let apiService;
|
|
53
60
|
let unfinalizedBlocksService;
|
|
54
|
-
beforeEach(() => {
|
|
61
|
+
beforeEach(async () => {
|
|
55
62
|
apiService = mockApiService();
|
|
56
|
-
unfinalizedBlocksService = new unfinalizedBlocks_service_1.UnfinalizedBlocksService(apiService, { unfinalizedBlocks: true },
|
|
57
|
-
unfinalizedBlocksService.init(
|
|
63
|
+
unfinalizedBlocksService = new unfinalizedBlocks_service_1.UnfinalizedBlocksService(apiService, { unfinalizedBlocks: true }, mockStoreCache());
|
|
64
|
+
await unfinalizedBlocksService.init(() => Promise.resolve());
|
|
58
65
|
});
|
|
59
66
|
afterEach(() => {
|
|
60
67
|
unfinalizedBlocksService.unfinalizedBlocks = {};
|
|
@@ -70,8 +77,8 @@ describe('UnfinalizedBlocksService', () => {
|
|
|
70
77
|
});
|
|
71
78
|
it('keeps track of unfinalized blocks', async () => {
|
|
72
79
|
unfinalizedBlocksService.registerFinalizedBlock(apiService.api.getBlockByHeightOrHash(110));
|
|
73
|
-
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(111, '0xabc111')
|
|
74
|
-
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(112, '0xabc112')
|
|
80
|
+
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(111, '0xabc111'));
|
|
81
|
+
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(112, '0xabc112'));
|
|
75
82
|
expect(unfinalizedBlocksService.unfinalizedBlocks).toEqual([
|
|
76
83
|
[111, '0xabc111'],
|
|
77
84
|
[112, '0xabc112'],
|
|
@@ -79,86 +86,86 @@ describe('UnfinalizedBlocksService', () => {
|
|
|
79
86
|
});
|
|
80
87
|
it('doesnt keep track of finalized blocks', async () => {
|
|
81
88
|
unfinalizedBlocksService.registerFinalizedBlock(apiService.api.getBlockByHeightOrHash(120));
|
|
82
|
-
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(111, '0xabc111')
|
|
83
|
-
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(112, '0xabc112')
|
|
89
|
+
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(111, '0xabc111'));
|
|
90
|
+
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(112, '0xabc112'));
|
|
84
91
|
expect(unfinalizedBlocksService.unfinalizedBlocks).toEqual([]);
|
|
85
92
|
});
|
|
86
93
|
it('can process unfinalized blocks', async () => {
|
|
87
94
|
unfinalizedBlocksService.registerFinalizedBlock(apiService.api.getBlockByHeightOrHash(110));
|
|
88
|
-
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(111, '0xabc111')
|
|
89
|
-
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(112, '0xabc112')
|
|
95
|
+
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(111, '0xabc111'));
|
|
96
|
+
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(112, '0xabc112'));
|
|
90
97
|
unfinalizedBlocksService.registerFinalizedBlock(mockBlock(112, '0xabc112', '0xabc111'));
|
|
91
|
-
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(113, '0xabc113')
|
|
98
|
+
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(113, '0xabc113'));
|
|
92
99
|
expect(unfinalizedBlocksService.unfinalizedBlocks).toEqual([
|
|
93
100
|
[113, '0xabc113'],
|
|
94
101
|
]);
|
|
95
102
|
});
|
|
96
103
|
it('can handle a fork and rewind to the last finalized height', async () => {
|
|
97
104
|
unfinalizedBlocksService.registerFinalizedBlock(apiService.api.getBlockByHeightOrHash(110));
|
|
98
|
-
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(111, '0xabc111')
|
|
99
|
-
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(112, '0xabc112')
|
|
105
|
+
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(111, '0xabc111'));
|
|
106
|
+
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(112, '0xabc112'));
|
|
100
107
|
// Forked block
|
|
101
108
|
unfinalizedBlocksService.registerFinalizedBlock(mockBlock(112, '0xabc112f', '0xabc111'));
|
|
102
|
-
const res = await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(113, '0xabc113')
|
|
109
|
+
const res = await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(113, '0xabc113'));
|
|
103
110
|
// Last valid block
|
|
104
111
|
expect(res).toBe(111);
|
|
105
112
|
// After this the call stack is something like:
|
|
106
113
|
// indexerManager -> blockDispatcher -> project -> project -> reindex -> blockDispatcher.resetUnfinalizedBlocks
|
|
107
|
-
|
|
114
|
+
unfinalizedBlocksService.resetUnfinalizedBlocks();
|
|
108
115
|
expect(unfinalizedBlocksService.unfinalizedBlocks).toEqual([]);
|
|
109
116
|
});
|
|
110
117
|
it('can handle a fork when some unfinalized blocks are invalid', async () => {
|
|
111
118
|
unfinalizedBlocksService.registerFinalizedBlock(apiService.api.getBlockByHeightOrHash(110));
|
|
112
|
-
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(111, '0xabc111')
|
|
113
|
-
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(112, '0xabc112')
|
|
114
|
-
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(113, '0xabc113')
|
|
115
|
-
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(114, '0xabc114')
|
|
116
|
-
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(115, '0xabc115')
|
|
117
|
-
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(116, '0xabc116')
|
|
119
|
+
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(111, '0xabc111'));
|
|
120
|
+
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(112, '0xabc112'));
|
|
121
|
+
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(113, '0xabc113'));
|
|
122
|
+
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(114, '0xabc114'));
|
|
123
|
+
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(115, '0xabc115'));
|
|
124
|
+
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(116, '0xabc116'));
|
|
118
125
|
// Forked block
|
|
119
126
|
unfinalizedBlocksService.registerFinalizedBlock(mockBlock(113, '0xabc113f', '0xabc112'));
|
|
120
|
-
const res = await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(117, '0xabc117')
|
|
127
|
+
const res = await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(117, '0xabc117'));
|
|
121
128
|
// Last valid block
|
|
122
129
|
expect(res).toBe(112);
|
|
123
130
|
});
|
|
124
131
|
it('can handle a fork when all unfinalized blocks are invalid', async () => {
|
|
125
132
|
unfinalizedBlocksService.registerFinalizedBlock(apiService.api.getBlockByHeightOrHash(110));
|
|
126
|
-
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(111, '0xabc111')
|
|
127
|
-
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(112, '0xabc112')
|
|
133
|
+
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(111, '0xabc111'));
|
|
134
|
+
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(112, '0xabc112'));
|
|
128
135
|
// Forked block
|
|
129
136
|
unfinalizedBlocksService.registerFinalizedBlock(mockBlock(111, '0xabc111f', '0xabc110'));
|
|
130
|
-
const res = await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(113, '0xabc113')
|
|
137
|
+
const res = await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(113, '0xabc113'));
|
|
131
138
|
// Last valid block
|
|
132
139
|
expect(res).toBe(110);
|
|
133
140
|
});
|
|
134
141
|
it('can handle a fork and when unfinalized blocks < finalized head', async () => {
|
|
135
142
|
unfinalizedBlocksService.registerFinalizedBlock(apiService.api.getBlockByHeightOrHash(110));
|
|
136
|
-
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(111, '0xabc111')
|
|
137
|
-
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(112, '0xabc112')
|
|
143
|
+
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(111, '0xabc111'));
|
|
144
|
+
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(112, '0xabc112'));
|
|
138
145
|
// Forked block
|
|
139
146
|
unfinalizedBlocksService.registerFinalizedBlock(mockBlock(120, '0xabc120f', '0xabc119f'));
|
|
140
|
-
const res = await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(113, '0xabc113')
|
|
147
|
+
const res = await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(113, '0xabc113'));
|
|
141
148
|
// Last valid block
|
|
142
149
|
expect(res).toBe(110);
|
|
143
150
|
});
|
|
144
151
|
it('can handle a fork and when unfinalized blocks < finalized head 2', async () => {
|
|
145
152
|
unfinalizedBlocksService.registerFinalizedBlock(apiService.api.getBlockByHeightOrHash(110));
|
|
146
153
|
unfinalizedBlocksService.lastCheckedBlockHeight = 110;
|
|
147
|
-
await unfinalizedBlocksService.registerUnfinalizedBlock(111, '0xabc111'
|
|
148
|
-
await unfinalizedBlocksService.registerUnfinalizedBlock(112, '0xabc112'
|
|
154
|
+
await unfinalizedBlocksService.registerUnfinalizedBlock(111, '0xabc111');
|
|
155
|
+
await unfinalizedBlocksService.registerUnfinalizedBlock(112, '0xabc112');
|
|
149
156
|
// Forked block
|
|
150
157
|
unfinalizedBlocksService.registerFinalizedBlock(mockBlock(120, '0xabc120f', '0xabc119f'));
|
|
151
|
-
const res = await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(113, '0xabc113')
|
|
158
|
+
const res = await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(113, '0xabc113'));
|
|
152
159
|
// Last valid block
|
|
153
160
|
expect(res).toBe(110);
|
|
154
161
|
});
|
|
155
162
|
it('can handle a fork and when unfinalized blocks < finalized head with a large difference', async () => {
|
|
156
163
|
unfinalizedBlocksService.registerFinalizedBlock(apiService.api.getBlockByHeightOrHash(110));
|
|
157
|
-
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(111, '0xabc111')
|
|
158
|
-
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(112, '0xabc112')
|
|
164
|
+
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(111, '0xabc111'));
|
|
165
|
+
await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(112, '0xabc112'));
|
|
159
166
|
// Forked block
|
|
160
167
|
unfinalizedBlocksService.registerFinalizedBlock(mockBlock(1200, '0xabc1200f', '0xabc1199f'));
|
|
161
|
-
const res = await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(113, '0xabc113')
|
|
168
|
+
const res = await unfinalizedBlocksService.processUnfinalizedBlocks(mockBlock(113, '0xabc113'));
|
|
162
169
|
// Last valid block
|
|
163
170
|
expect(res).toBe(110);
|
|
164
171
|
});
|
|
@@ -180,7 +187,7 @@ describe('UnfinalizedBlocksService', () => {
|
|
|
180
187
|
transaction: () => Promise.resolve({ commit: () => undefined }),
|
|
181
188
|
});
|
|
182
189
|
const reindex = jest.fn().mockReturnValue(Promise.resolve());
|
|
183
|
-
await unfinalizedBlocksService2.init(
|
|
190
|
+
await unfinalizedBlocksService2.init(reindex);
|
|
184
191
|
expect(reindex).toBeCalledWith(90);
|
|
185
192
|
expect(unfinalizedBlocksService2.lastCheckedBlockHeight).toBe(90);
|
|
186
193
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unfinalizedBlocks.spec.js","sourceRoot":"","sources":["../../src/indexer/unfinalizedBlocks.spec.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2020-2022 OnFinality Limited authors & contributors\n// SPDX-License-Identifier: Apache-2.0\n\nimport { Block } from '@ethersproject/abstract-provider';\nimport { MetadataRepo, ApiService } from '@subql/node-core';\nimport { EthereumBlock } from '@subql/types-ethereum';\nimport {\n METADATA_LAST_FINALIZED_PROCESSED_KEY,\n METADATA_UNFINALIZED_BLOCKS_KEY,\n UnfinalizedBlocksService,\n} from './unfinalizedBlocks.service';\n\n/* Notes:\n * Block hashes all have the format '0xabc' + block number\n * If they are forked they will have an `f` at the end\n */\n\nfunction mockApiService(): ApiService {\n const mockApi = {\n getFinalizedBlockHeight: jest.fn(() => 110),\n getRuntimeChain: jest.fn(() => 'ethereum'),\n getBlockByHeightOrHash: (heightOrHash: string | number): Block => {\n if (typeof heightOrHash === 'string') {\n const height = Number(\n heightOrHash.toString().replace('0xabc', '').replace('f', ''),\n );\n\n return {\n number: height,\n hash: heightOrHash,\n parentHash: `0xabc${height - 1}f`,\n } as unknown as Block;\n } else {\n const hash = `0xabc${heightOrHash}f`;\n return {\n number: heightOrHash,\n hash: hash,\n parentHash: `0xabc${heightOrHash - 1}f`,\n } as unknown as Block;\n }\n },\n };\n return {\n api: mockApi,\n } as any;\n}\n\nfunction getMockMetadata(): MetadataRepo {\n const data: Record<string, any> = {};\n return {\n upsert: ({ key, value }) => (data[key] = value),\n findOne: ({ where: { key } }) => ({ value: data[key] }),\n } as any;\n}\n\nfunction mockBlock(\n height: number,\n hash: string,\n parentHash?: string,\n): EthereumBlock {\n return {\n number: height,\n hash: hash,\n parentHash: parentHash,\n } as unknown as EthereumBlock;\n}\n\ndescribe('UnfinalizedBlocksService', () => {\n let apiService: ApiService;\n let unfinalizedBlocksService: UnfinalizedBlocksService;\n\n beforeEach(() => {\n apiService = mockApiService();\n unfinalizedBlocksService = new UnfinalizedBlocksService(\n apiService,\n { unfinalizedBlocks: true } as any,\n null,\n );\n\n unfinalizedBlocksService.init(getMockMetadata(), () => Promise.resolve());\n });\n\n afterEach(() => {\n (unfinalizedBlocksService as unknown as any).unfinalizedBlocks = {};\n });\n\n it('can set finalized block', () => {\n unfinalizedBlocksService.registerFinalizedBlock(\n apiService.api.getBlockByHeightOrHash(110),\n );\n\n expect((unfinalizedBlocksService as any).finalizedBlockNumber).toBe(110);\n });\n\n it('cant set a lower finalized block', () => {\n unfinalizedBlocksService.registerFinalizedBlock(\n apiService.api.getBlockByHeightOrHash(110),\n );\n\n unfinalizedBlocksService.registerFinalizedBlock(\n apiService.api.getBlockByHeightOrHash(99),\n );\n\n expect((unfinalizedBlocksService as any).finalizedBlockNumber).toBe(110);\n });\n\n it('keeps track of unfinalized blocks', async () => {\n unfinalizedBlocksService.registerFinalizedBlock(\n apiService.api.getBlockByHeightOrHash(110),\n );\n\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(111, '0xabc111'),\n null,\n );\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(112, '0xabc112'),\n null,\n );\n\n expect((unfinalizedBlocksService as any).unfinalizedBlocks).toEqual([\n [111, '0xabc111'],\n [112, '0xabc112'],\n ]);\n });\n\n it('doesnt keep track of finalized blocks', async () => {\n unfinalizedBlocksService.registerFinalizedBlock(\n apiService.api.getBlockByHeightOrHash(120),\n );\n\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(111, '0xabc111'),\n null,\n );\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(112, '0xabc112'),\n null,\n );\n\n expect((unfinalizedBlocksService as any).unfinalizedBlocks).toEqual([]);\n });\n\n it('can process unfinalized blocks', async () => {\n unfinalizedBlocksService.registerFinalizedBlock(\n apiService.api.getBlockByHeightOrHash(110),\n );\n\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(111, '0xabc111'),\n null,\n );\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(112, '0xabc112'),\n null,\n );\n\n unfinalizedBlocksService.registerFinalizedBlock(\n mockBlock(112, '0xabc112', '0xabc111') as unknown as Block,\n );\n\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(113, '0xabc113'),\n null,\n );\n\n expect((unfinalizedBlocksService as any).unfinalizedBlocks).toEqual([\n [113, '0xabc113'],\n ]);\n });\n\n it('can handle a fork and rewind to the last finalized height', async () => {\n unfinalizedBlocksService.registerFinalizedBlock(\n apiService.api.getBlockByHeightOrHash(110),\n );\n\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(111, '0xabc111'),\n null,\n );\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(112, '0xabc112'),\n null,\n );\n\n // Forked block\n unfinalizedBlocksService.registerFinalizedBlock(\n mockBlock(112, '0xabc112f', '0xabc111') as unknown as Block,\n );\n\n const res = await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(113, '0xabc113'),\n null,\n );\n\n // Last valid block\n expect(res).toBe(111);\n\n // After this the call stack is something like:\n // indexerManager -> blockDispatcher -> project -> project -> reindex -> blockDispatcher.resetUnfinalizedBlocks\n await unfinalizedBlocksService.resetUnfinalizedBlocks(null);\n\n expect((unfinalizedBlocksService as any).unfinalizedBlocks).toEqual([]);\n });\n\n it('can handle a fork when some unfinalized blocks are invalid', async () => {\n unfinalizedBlocksService.registerFinalizedBlock(\n apiService.api.getBlockByHeightOrHash(110),\n );\n\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(111, '0xabc111'),\n null,\n );\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(112, '0xabc112'),\n null,\n );\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(113, '0xabc113'),\n null,\n );\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(114, '0xabc114'),\n null,\n );\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(115, '0xabc115'),\n null,\n );\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(116, '0xabc116'),\n null,\n );\n\n // Forked block\n unfinalizedBlocksService.registerFinalizedBlock(\n mockBlock(113, '0xabc113f', '0xabc112') as unknown as Block,\n );\n\n const res = await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(117, '0xabc117'),\n null,\n );\n\n // Last valid block\n expect(res).toBe(112);\n });\n\n it('can handle a fork when all unfinalized blocks are invalid', async () => {\n unfinalizedBlocksService.registerFinalizedBlock(\n apiService.api.getBlockByHeightOrHash(110),\n );\n\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(111, '0xabc111'),\n null,\n );\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(112, '0xabc112'),\n null,\n );\n\n // Forked block\n unfinalizedBlocksService.registerFinalizedBlock(\n mockBlock(111, '0xabc111f', '0xabc110') as unknown as Block,\n );\n\n const res = await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(113, '0xabc113'),\n null,\n );\n\n // Last valid block\n expect(res).toBe(110);\n });\n\n it('can handle a fork and when unfinalized blocks < finalized head', async () => {\n unfinalizedBlocksService.registerFinalizedBlock(\n apiService.api.getBlockByHeightOrHash(110),\n );\n\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(111, '0xabc111'),\n null,\n );\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(112, '0xabc112'),\n null,\n );\n\n // Forked block\n unfinalizedBlocksService.registerFinalizedBlock(\n mockBlock(120, '0xabc120f', '0xabc119f') as unknown as Block,\n );\n\n const res = await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(113, '0xabc113'),\n null,\n );\n\n // Last valid block\n expect(res).toBe(110);\n });\n\n it('can handle a fork and when unfinalized blocks < finalized head 2', async () => {\n unfinalizedBlocksService.registerFinalizedBlock(\n apiService.api.getBlockByHeightOrHash(110),\n );\n\n (unfinalizedBlocksService as any).lastCheckedBlockHeight = 110;\n\n await (unfinalizedBlocksService as any).registerUnfinalizedBlock(\n 111,\n '0xabc111',\n null,\n );\n await (unfinalizedBlocksService as any).registerUnfinalizedBlock(\n 112,\n '0xabc112',\n null,\n );\n\n // Forked block\n unfinalizedBlocksService.registerFinalizedBlock(\n mockBlock(120, '0xabc120f', '0xabc119f') as unknown as Block,\n );\n\n const res = await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(113, '0xabc113'),\n null,\n );\n\n // Last valid block\n expect(res).toBe(110);\n });\n\n it('can handle a fork and when unfinalized blocks < finalized head with a large difference', async () => {\n unfinalizedBlocksService.registerFinalizedBlock(\n apiService.api.getBlockByHeightOrHash(110),\n );\n\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(111, '0xabc111'),\n null,\n );\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(112, '0xabc112'),\n null,\n );\n\n // Forked block\n unfinalizedBlocksService.registerFinalizedBlock(\n mockBlock(1200, '0xabc1200f', '0xabc1199f') as unknown as Block,\n );\n\n const res = await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(113, '0xabc113'),\n null,\n );\n\n // Last valid block\n expect(res).toBe(110);\n });\n\n it('can rewind any unfinalized blocks when restarted and unfinalized blocks is disabled', async () => {\n const metadata = getMockMetadata();\n\n metadata.upsert({\n key: METADATA_UNFINALIZED_BLOCKS_KEY,\n value: JSON.stringify([\n [90, '0xabcd'],\n [91, '0xabc91'],\n [92, '0xabc92'],\n ]),\n });\n\n metadata.upsert({\n key: METADATA_LAST_FINALIZED_PROCESSED_KEY,\n value: 90,\n });\n\n const unfinalizedBlocksService2 = new UnfinalizedBlocksService(\n apiService,\n { unfinalizedBlocks: false } as any,\n {\n transaction: () => Promise.resolve({ commit: () => undefined }),\n } as any,\n );\n\n const reindex = jest.fn().mockReturnValue(Promise.resolve());\n\n await unfinalizedBlocksService2.init(metadata, reindex);\n\n expect(reindex).toBeCalledWith(90);\n expect((unfinalizedBlocksService2 as any).lastCheckedBlockHeight).toBe(90);\n });\n});\n"]}
|
|
1
|
+
{"version":3,"file":"unfinalizedBlocks.spec.js","sourceRoot":"","sources":["../../src/indexer/unfinalizedBlocks.spec.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2020-2022 OnFinality Limited authors & contributors\n// SPDX-License-Identifier: Apache-2.0\n\nimport { Block } from '@ethersproject/abstract-provider';\nimport {\n ApiService,\n CacheMetadataModel,\n StoreCacheService,\n} from '@subql/node-core';\nimport { EthereumBlock } from '@subql/types-ethereum';\nimport {\n METADATA_LAST_FINALIZED_PROCESSED_KEY,\n METADATA_UNFINALIZED_BLOCKS_KEY,\n UnfinalizedBlocksService,\n} from './unfinalizedBlocks.service';\n\n/* Notes:\n * Block hashes all have the format '0xabc' + block number\n * If they are forked they will have an `f` at the end\n */\n\nfunction mockApiService(): ApiService {\n const mockApi = {\n getFinalizedBlockHeight: jest.fn(() => 110),\n getRuntimeChain: jest.fn(() => 'ethereum'),\n getBlockByHeightOrHash: (heightOrHash: string | number): Block => {\n if (typeof heightOrHash === 'string') {\n const height = Number(\n heightOrHash.toString().replace('0xabc', '').replace('f', ''),\n );\n\n return {\n number: height,\n hash: heightOrHash,\n parentHash: `0xabc${height - 1}f`,\n } as unknown as Block;\n } else {\n const hash = `0xabc${heightOrHash}f`;\n return {\n number: heightOrHash,\n hash: hash,\n parentHash: `0xabc${heightOrHash - 1}f`,\n } as unknown as Block;\n }\n },\n };\n return {\n api: mockApi,\n } as any;\n}\n\nfunction getMockMetadata(): any {\n const data: Record<string, any> = {};\n return {\n upsert: ({ key, value }) => (data[key] = value),\n findOne: ({ where: { key } }) => ({ value: data[key] }),\n findByPk: (key: string) => data[key],\n } as any;\n}\n\nfunction mockStoreCache(): StoreCacheService {\n return {\n metadata: new CacheMetadataModel(getMockMetadata()),\n } as StoreCacheService;\n}\n\nfunction mockBlock(\n height: number,\n hash: string,\n parentHash?: string,\n): EthereumBlock {\n return {\n number: height,\n hash: hash,\n parentHash: parentHash,\n } as unknown as EthereumBlock;\n}\n\ndescribe('UnfinalizedBlocksService', () => {\n let apiService: ApiService;\n let unfinalizedBlocksService: UnfinalizedBlocksService;\n\n beforeEach(async () => {\n apiService = mockApiService();\n unfinalizedBlocksService = new UnfinalizedBlocksService(\n apiService,\n { unfinalizedBlocks: true } as any,\n mockStoreCache(),\n );\n\n await unfinalizedBlocksService.init(() => Promise.resolve());\n });\n\n afterEach(() => {\n (unfinalizedBlocksService as unknown as any).unfinalizedBlocks = {};\n });\n\n it('can set finalized block', () => {\n unfinalizedBlocksService.registerFinalizedBlock(\n apiService.api.getBlockByHeightOrHash(110),\n );\n\n expect((unfinalizedBlocksService as any).finalizedBlockNumber).toBe(110);\n });\n\n it('cant set a lower finalized block', () => {\n unfinalizedBlocksService.registerFinalizedBlock(\n apiService.api.getBlockByHeightOrHash(110),\n );\n\n unfinalizedBlocksService.registerFinalizedBlock(\n apiService.api.getBlockByHeightOrHash(99),\n );\n\n expect((unfinalizedBlocksService as any).finalizedBlockNumber).toBe(110);\n });\n\n it('keeps track of unfinalized blocks', async () => {\n unfinalizedBlocksService.registerFinalizedBlock(\n apiService.api.getBlockByHeightOrHash(110),\n );\n\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(111, '0xabc111'),\n );\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(112, '0xabc112'),\n );\n\n expect((unfinalizedBlocksService as any).unfinalizedBlocks).toEqual([\n [111, '0xabc111'],\n [112, '0xabc112'],\n ]);\n });\n\n it('doesnt keep track of finalized blocks', async () => {\n unfinalizedBlocksService.registerFinalizedBlock(\n apiService.api.getBlockByHeightOrHash(120),\n );\n\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(111, '0xabc111'),\n );\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(112, '0xabc112'),\n );\n\n expect((unfinalizedBlocksService as any).unfinalizedBlocks).toEqual([]);\n });\n\n it('can process unfinalized blocks', async () => {\n unfinalizedBlocksService.registerFinalizedBlock(\n apiService.api.getBlockByHeightOrHash(110),\n );\n\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(111, '0xabc111'),\n );\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(112, '0xabc112'),\n );\n\n unfinalizedBlocksService.registerFinalizedBlock(\n mockBlock(112, '0xabc112', '0xabc111') as unknown as Block,\n );\n\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(113, '0xabc113'),\n );\n\n expect((unfinalizedBlocksService as any).unfinalizedBlocks).toEqual([\n [113, '0xabc113'],\n ]);\n });\n\n it('can handle a fork and rewind to the last finalized height', async () => {\n unfinalizedBlocksService.registerFinalizedBlock(\n apiService.api.getBlockByHeightOrHash(110),\n );\n\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(111, '0xabc111'),\n );\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(112, '0xabc112'),\n );\n\n // Forked block\n unfinalizedBlocksService.registerFinalizedBlock(\n mockBlock(112, '0xabc112f', '0xabc111') as unknown as Block,\n );\n\n const res = await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(113, '0xabc113'),\n );\n\n // Last valid block\n expect(res).toBe(111);\n\n // After this the call stack is something like:\n // indexerManager -> blockDispatcher -> project -> project -> reindex -> blockDispatcher.resetUnfinalizedBlocks\n unfinalizedBlocksService.resetUnfinalizedBlocks();\n\n expect((unfinalizedBlocksService as any).unfinalizedBlocks).toEqual([]);\n });\n\n it('can handle a fork when some unfinalized blocks are invalid', async () => {\n unfinalizedBlocksService.registerFinalizedBlock(\n apiService.api.getBlockByHeightOrHash(110),\n );\n\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(111, '0xabc111'),\n );\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(112, '0xabc112'),\n );\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(113, '0xabc113'),\n );\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(114, '0xabc114'),\n );\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(115, '0xabc115'),\n );\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(116, '0xabc116'),\n );\n\n // Forked block\n unfinalizedBlocksService.registerFinalizedBlock(\n mockBlock(113, '0xabc113f', '0xabc112') as unknown as Block,\n );\n\n const res = await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(117, '0xabc117'),\n );\n\n // Last valid block\n expect(res).toBe(112);\n });\n\n it('can handle a fork when all unfinalized blocks are invalid', async () => {\n unfinalizedBlocksService.registerFinalizedBlock(\n apiService.api.getBlockByHeightOrHash(110),\n );\n\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(111, '0xabc111'),\n );\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(112, '0xabc112'),\n );\n\n // Forked block\n unfinalizedBlocksService.registerFinalizedBlock(\n mockBlock(111, '0xabc111f', '0xabc110') as unknown as Block,\n );\n\n const res = await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(113, '0xabc113'),\n );\n\n // Last valid block\n expect(res).toBe(110);\n });\n\n it('can handle a fork and when unfinalized blocks < finalized head', async () => {\n unfinalizedBlocksService.registerFinalizedBlock(\n apiService.api.getBlockByHeightOrHash(110),\n );\n\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(111, '0xabc111'),\n );\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(112, '0xabc112'),\n );\n\n // Forked block\n unfinalizedBlocksService.registerFinalizedBlock(\n mockBlock(120, '0xabc120f', '0xabc119f') as unknown as Block,\n );\n\n const res = await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(113, '0xabc113'),\n );\n\n // Last valid block\n expect(res).toBe(110);\n });\n\n it('can handle a fork and when unfinalized blocks < finalized head 2', async () => {\n unfinalizedBlocksService.registerFinalizedBlock(\n apiService.api.getBlockByHeightOrHash(110),\n );\n\n (unfinalizedBlocksService as any).lastCheckedBlockHeight = 110;\n\n await (unfinalizedBlocksService as any).registerUnfinalizedBlock(\n 111,\n '0xabc111',\n );\n await (unfinalizedBlocksService as any).registerUnfinalizedBlock(\n 112,\n '0xabc112',\n );\n\n // Forked block\n unfinalizedBlocksService.registerFinalizedBlock(\n mockBlock(120, '0xabc120f', '0xabc119f') as unknown as Block,\n );\n\n const res = await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(113, '0xabc113'),\n );\n\n // Last valid block\n expect(res).toBe(110);\n });\n\n it('can handle a fork and when unfinalized blocks < finalized head with a large difference', async () => {\n unfinalizedBlocksService.registerFinalizedBlock(\n apiService.api.getBlockByHeightOrHash(110),\n );\n\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(111, '0xabc111'),\n );\n await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(112, '0xabc112'),\n );\n\n // Forked block\n unfinalizedBlocksService.registerFinalizedBlock(\n mockBlock(1200, '0xabc1200f', '0xabc1199f') as unknown as Block,\n );\n\n const res = await unfinalizedBlocksService.processUnfinalizedBlocks(\n mockBlock(113, '0xabc113'),\n );\n\n // Last valid block\n expect(res).toBe(110);\n });\n\n it('can rewind any unfinalized blocks when restarted and unfinalized blocks is disabled', async () => {\n const metadata = getMockMetadata();\n\n metadata.upsert({\n key: METADATA_UNFINALIZED_BLOCKS_KEY,\n value: JSON.stringify([\n [90, '0xabcd'],\n [91, '0xabc91'],\n [92, '0xabc92'],\n ]),\n });\n\n metadata.upsert({\n key: METADATA_LAST_FINALIZED_PROCESSED_KEY,\n value: 90,\n });\n\n const unfinalizedBlocksService2 = new UnfinalizedBlocksService(\n apiService,\n { unfinalizedBlocks: false } as any,\n {\n transaction: () => Promise.resolve({ commit: () => undefined }),\n } as any,\n );\n\n const reindex = jest.fn().mockReturnValue(Promise.resolve());\n\n await unfinalizedBlocksService2.init(reindex);\n\n expect(reindex).toBeCalledWith(90);\n expect((unfinalizedBlocksService2 as any).lastCheckedBlockHeight).toBe(90);\n });\n});\n"]}
|
|
@@ -5,12 +5,26 @@ declare function processBlock(height: number): Promise<ProcessBlockResponse>;
|
|
|
5
5
|
declare function numFetchedBlocks(): Promise<number>;
|
|
6
6
|
declare function numFetchingBlocks(): Promise<number>;
|
|
7
7
|
declare function getStatus(): Promise<WorkerStatusResponse>;
|
|
8
|
-
declare function
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
8
|
+
declare function getMemoryLeft(): Promise<number>;
|
|
9
|
+
declare function waitForWorkerBatchSize(heapSizeInBytes: number): Promise<void>;
|
|
10
|
+
declare type InitWorker = typeof initWorker;
|
|
11
|
+
declare type FetchBlock = typeof fetchBlock;
|
|
12
|
+
declare type ProcessBlock = typeof processBlock;
|
|
13
|
+
declare type NumFetchedBlocks = typeof numFetchedBlocks;
|
|
14
|
+
declare type NumFetchingBlocks = typeof numFetchingBlocks;
|
|
15
|
+
declare type GetWorkerStatus = typeof getStatus;
|
|
16
|
+
declare type GetMemoryLeft = typeof getMemoryLeft;
|
|
17
|
+
declare type WaitForWorkerBatchSize = typeof waitForWorkerBatchSize;
|
|
18
|
+
export declare type IIndexerWorker = {
|
|
19
|
+
processBlock: ProcessBlock;
|
|
20
|
+
fetchBlock: FetchBlock;
|
|
21
|
+
numFetchedBlocks: NumFetchedBlocks;
|
|
22
|
+
numFetchingBlocks: NumFetchingBlocks;
|
|
23
|
+
getStatus: GetWorkerStatus;
|
|
24
|
+
getMemoryLeft: GetMemoryLeft;
|
|
25
|
+
waitForWorkerBatchSize: WaitForWorkerBatchSize;
|
|
26
|
+
};
|
|
27
|
+
export declare type IInitIndexerWorker = IIndexerWorker & {
|
|
28
|
+
initWorker: InitWorker;
|
|
29
|
+
};
|
|
16
30
|
export {};
|