@subql/node-ethereum 5.4.1-1 → 5.4.1-2
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 +2 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/blockchain.service.d.ts +30 -0
- package/dist/blockchain.service.js +112 -0
- package/dist/blockchain.service.js.map +1 -0
- package/dist/{indexer/project.service.test.js → blockchain.service.test.js} +7 -7
- package/dist/blockchain.service.test.js.map +1 -0
- package/dist/ethereum/api.service.ethereum.d.ts +2 -2
- package/dist/ethereum/api.service.ethereum.js +11 -8
- package/dist/ethereum/api.service.ethereum.js.map +1 -1
- package/dist/ethereum/api.service.ethereum.test.js +10 -2
- package/dist/ethereum/api.service.ethereum.test.js.map +1 -1
- package/dist/ethereum/ethers/celo/celo-provider.d.ts +18 -18
- package/dist/ethereum/ethers/op/op-provider.d.ts +20 -20
- package/dist/indexer/dictionary/ethDictionary.service.js +1 -1
- package/dist/indexer/dictionary/ethDictionary.service.js.map +1 -1
- package/dist/indexer/fetch.module.js +30 -24
- package/dist/indexer/fetch.module.js.map +1 -1
- package/dist/indexer/indexer.manager.d.ts +3 -5
- package/dist/indexer/indexer.manager.js +14 -12
- package/dist/indexer/indexer.manager.js.map +1 -1
- package/dist/indexer/unfinalizedBlocks.service.d.ts +3 -6
- package/dist/indexer/unfinalizedBlocks.service.js +10 -41
- package/dist/indexer/unfinalizedBlocks.service.js.map +1 -1
- package/dist/indexer/unfinalizedBlocks.service.spec.js +11 -9
- package/dist/indexer/unfinalizedBlocks.service.spec.js.map +1 -1
- package/dist/indexer/worker/worker-fetch.module.js +7 -18
- package/dist/indexer/worker/worker-fetch.module.js.map +1 -1
- package/dist/indexer/worker/worker.js.map +1 -1
- package/dist/indexer/worker/worker.service.js +1 -0
- package/dist/indexer/worker/worker.service.js.map +1 -1
- package/dist/init.js +1 -2
- package/dist/init.js.map +1 -1
- package/dist/subcommands/reindex.module.js +10 -11
- package/dist/subcommands/reindex.module.js.map +1 -1
- package/dist/subcommands/testing.module.js +14 -31
- package/dist/subcommands/testing.module.js.map +1 -1
- package/dist/subcommands/testing.service.d.ts +1 -3
- package/dist/subcommands/testing.service.js +0 -3
- package/dist/subcommands/testing.service.js.map +1 -1
- package/package.json +12 -12
- package/dist/indexer/blockDispatcher/block-dispatcher.service.d.ts +0 -15
- package/dist/indexer/blockDispatcher/block-dispatcher.service.js +0 -51
- package/dist/indexer/blockDispatcher/block-dispatcher.service.js.map +0 -1
- package/dist/indexer/blockDispatcher/ethereum-block-dispatcher.d.ts +0 -5
- package/dist/indexer/blockDispatcher/ethereum-block-dispatcher.js +0 -5
- package/dist/indexer/blockDispatcher/ethereum-block-dispatcher.js.map +0 -1
- package/dist/indexer/blockDispatcher/index.d.ts +0 -4
- package/dist/indexer/blockDispatcher/index.js +0 -10
- package/dist/indexer/blockDispatcher/index.js.map +0 -1
- package/dist/indexer/blockDispatcher/worker-block-dispatcher.service.d.ts +0 -17
- package/dist/indexer/blockDispatcher/worker-block-dispatcher.service.js +0 -48
- package/dist/indexer/blockDispatcher/worker-block-dispatcher.service.js.map +0 -1
- package/dist/indexer/ds-processor.service.d.ts +0 -5
- package/dist/indexer/ds-processor.service.js +0 -22
- package/dist/indexer/ds-processor.service.js.map +0 -1
- package/dist/indexer/dynamic-ds.service.d.ts +0 -8
- package/dist/indexer/dynamic-ds.service.js +0 -69
- package/dist/indexer/dynamic-ds.service.js.map +0 -1
- package/dist/indexer/fetch.service.d.ts +0 -20
- package/dist/indexer/fetch.service.js +0 -74
- package/dist/indexer/fetch.service.js.map +0 -1
- package/dist/indexer/project.service.d.ts +0 -16
- package/dist/indexer/project.service.js +0 -77
- package/dist/indexer/project.service.js.map +0 -1
- package/dist/indexer/project.service.test.js.map +0 -1
- /package/dist/{indexer/project.service.test.d.ts → blockchain.service.test.d.ts} +0 -0
|
@@ -7,19 +7,19 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
8
8
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
9
9
|
};
|
|
10
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
11
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
12
|
+
};
|
|
10
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
14
|
exports.FetchModule = void 0;
|
|
15
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
12
16
|
const common_1 = require("@nestjs/common");
|
|
13
17
|
const event_emitter_1 = require("@nestjs/event-emitter");
|
|
14
18
|
const node_core_1 = require("@subql/node-core");
|
|
19
|
+
const blockchain_service_1 = require("../blockchain.service");
|
|
15
20
|
const api_service_ethereum_1 = require("../ethereum/api.service.ethereum");
|
|
16
|
-
const blockDispatcher_1 = require("./blockDispatcher");
|
|
17
21
|
const ethDictionary_service_1 = require("./dictionary/ethDictionary.service");
|
|
18
|
-
const ds_processor_service_1 = require("./ds-processor.service");
|
|
19
|
-
const dynamic_ds_service_1 = require("./dynamic-ds.service");
|
|
20
|
-
const fetch_service_1 = require("./fetch.service");
|
|
21
22
|
const indexer_manager_1 = require("./indexer.manager");
|
|
22
|
-
const project_service_1 = require("./project.service");
|
|
23
23
|
const unfinalizedBlocks_service_1 = require("./unfinalizedBlocks.service");
|
|
24
24
|
let FetchModule = class FetchModule {
|
|
25
25
|
};
|
|
@@ -29,12 +29,8 @@ exports.FetchModule = FetchModule = __decorate([
|
|
|
29
29
|
imports: [node_core_1.CoreModule],
|
|
30
30
|
providers: [
|
|
31
31
|
{
|
|
32
|
-
provide:
|
|
33
|
-
useFactory:
|
|
34
|
-
const apiService = new api_service_ethereum_1.EthereumApiService(project, connectionPoolService, eventEmitter, nodeConfig);
|
|
35
|
-
await apiService.init();
|
|
36
|
-
return apiService;
|
|
37
|
-
},
|
|
32
|
+
provide: 'APIService',
|
|
33
|
+
useFactory: api_service_ethereum_1.EthereumApiService.init,
|
|
38
34
|
inject: [
|
|
39
35
|
'ISubqueryProject',
|
|
40
36
|
node_core_1.ConnectionPoolService,
|
|
@@ -42,39 +38,49 @@ exports.FetchModule = FetchModule = __decorate([
|
|
|
42
38
|
node_core_1.NodeConfig,
|
|
43
39
|
],
|
|
44
40
|
},
|
|
41
|
+
{
|
|
42
|
+
provide: 'IBlockchainService',
|
|
43
|
+
useClass: blockchain_service_1.BlockchainService,
|
|
44
|
+
},
|
|
45
45
|
indexer_manager_1.IndexerManager,
|
|
46
46
|
{
|
|
47
47
|
provide: 'IBlockDispatcher',
|
|
48
|
-
useFactory: (nodeConfig, eventEmitter, projectService, projectUpgradeService,
|
|
49
|
-
? new
|
|
50
|
-
: new
|
|
48
|
+
useFactory: (nodeConfig, eventEmitter, projectService, projectUpgradeService, cacheService, storeService, storeModelProvider, poiSyncService, project, dynamicDsService, unfinalizedBlocks, connectionPoolState, blockchainService, indexerManager, monitorService) => nodeConfig.workers
|
|
49
|
+
? new node_core_1.WorkerBlockDispatcher(nodeConfig, eventEmitter, projectService, projectUpgradeService, storeService, storeModelProvider, cacheService, poiSyncService, dynamicDsService, unfinalizedBlocks, connectionPoolState, project, blockchainService, node_path_1.default.resolve(__dirname, '../../dist/indexer/worker/worker.js'), [], monitorService)
|
|
50
|
+
: new node_core_1.BlockDispatcher(nodeConfig, eventEmitter, projectService, projectUpgradeService, storeService, storeModelProvider, poiSyncService, project, blockchainService, indexerManager),
|
|
51
51
|
inject: [
|
|
52
52
|
node_core_1.NodeConfig,
|
|
53
53
|
event_emitter_1.EventEmitter2,
|
|
54
54
|
'IProjectService',
|
|
55
55
|
'IProjectUpgradeService',
|
|
56
|
-
node_core_1.ApiService,
|
|
57
|
-
indexer_manager_1.IndexerManager,
|
|
58
56
|
node_core_1.InMemoryCacheService,
|
|
59
57
|
node_core_1.StoreService,
|
|
60
58
|
'IStoreModelProvider',
|
|
61
59
|
node_core_1.PoiSyncService,
|
|
62
60
|
'ISubqueryProject',
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
node_core_1.DynamicDsService,
|
|
62
|
+
'IUnfinalizedBlocksService',
|
|
65
63
|
node_core_1.ConnectionPoolStateManager,
|
|
64
|
+
'IBlockchainService',
|
|
65
|
+
indexer_manager_1.IndexerManager,
|
|
66
66
|
node_core_1.MonitorService,
|
|
67
67
|
],
|
|
68
68
|
},
|
|
69
|
-
|
|
70
|
-
ethDictionary_service_1.EthDictionaryService,
|
|
71
|
-
ds_processor_service_1.DsProcessorService,
|
|
72
|
-
dynamic_ds_service_1.DynamicDsService,
|
|
69
|
+
node_core_1.FetchService,
|
|
73
70
|
{
|
|
74
|
-
|
|
71
|
+
provide: node_core_1.DictionaryService,
|
|
72
|
+
useClass: ethDictionary_service_1.EthDictionaryService,
|
|
73
|
+
},
|
|
74
|
+
node_core_1.DsProcessorService,
|
|
75
|
+
node_core_1.DynamicDsService,
|
|
76
|
+
{
|
|
77
|
+
useClass: node_core_1.ProjectService,
|
|
75
78
|
provide: 'IProjectService',
|
|
76
79
|
},
|
|
77
|
-
|
|
80
|
+
{
|
|
81
|
+
provide: 'IUnfinalizedBlocksService',
|
|
82
|
+
useClass: unfinalizedBlocks_service_1.UnfinalizedBlocksService,
|
|
83
|
+
},
|
|
78
84
|
],
|
|
79
85
|
})
|
|
80
86
|
], FetchModule);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch.module.js","sourceRoot":"","sources":["../../src/indexer/fetch.module.ts"],"names":[],"mappings":";AAAA,8DAA8D;AAC9D,mCAAmC
|
|
1
|
+
{"version":3,"file":"fetch.module.js","sourceRoot":"","sources":["../../src/indexer/fetch.module.ts"],"names":[],"mappings":";AAAA,8DAA8D;AAC9D,mCAAmC;;;;;;;;;;;;AAEnC,0DAA6B;AAC7B,2CAAwC;AACxC,yDAAsD;AAEtD,gDAkB0B;AAC1B,8DAA0D;AAG1D,2EAAsE;AACtE,8EAA0E;AAC1E,uDAAmD;AACnD,2EAAuE;AAyGhE,IAAM,WAAW,GAAjB,MAAM,WAAW;CAAG,CAAA;AAAd,kCAAW;sBAAX,WAAW;IAvGvB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,sBAAU,CAAC;QACrB,SAAS,EAAE;YACT;gBACE,OAAO,EAAE,YAAY;gBACrB,UAAU,EAAE,yCAAkB,CAAC,IAAI;gBACnC,MAAM,EAAE;oBACN,kBAAkB;oBAClB,iCAAqB;oBACrB,6BAAa;oBACb,sBAAU;iBACX;aACF;YACD;gBACE,OAAO,EAAE,oBAAoB;gBAC7B,QAAQ,EAAE,sCAAiB;aAC5B;YACD,gCAAc;YACd;gBACE,OAAO,EAAE,kBAAkB;gBAC3B,UAAU,EAAE,CACV,UAAsB,EACtB,YAA2B,EAC3B,cAAuD,EACvD,qBAA6C,EAC7C,YAAkC,EAClC,YAA0B,EAC1B,kBAAuC,EACvC,cAA8B,EAC9B,OAAwB,EACxB,gBAA2D,EAC3D,iBAA2C,EAC3C,mBAAsE,EACtE,iBAAoC,EACpC,cAA8B,EAC9B,cAA+B,EAC/B,EAAE,CACF,UAAU,CAAC,OAAO;oBAChB,CAAC,CAAC,IAAI,iCAAqB,CACvB,UAAU,EACV,YAAY,EACZ,cAAc,EACd,qBAAqB,EACrB,YAAY,EACZ,kBAAkB,EAClB,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,OAAO,EACP,iBAAiB,EACjB,mBAAI,CAAC,OAAO,CAAC,SAAS,EAAE,qCAAqC,CAAC,EAC9D,EAAE,EACF,cAAc,CACf;oBACH,CAAC,CAAC,IAAI,2BAAe,CACjB,UAAU,EACV,YAAY,EACZ,cAAc,EACd,qBAAqB,EACrB,YAAY,EACZ,kBAAkB,EAClB,cAAc,EACd,OAAO,EACP,iBAAiB,EACjB,cAAc,CACf;gBACP,MAAM,EAAE;oBACN,sBAAU;oBACV,6BAAa;oBACb,iBAAiB;oBACjB,wBAAwB;oBACxB,gCAAoB;oBACpB,wBAAY;oBACZ,qBAAqB;oBACrB,0BAAc;oBACd,kBAAkB;oBAClB,4BAAgB;oBAChB,2BAA2B;oBAC3B,sCAA0B;oBAC1B,oBAAoB;oBACpB,gCAAc;oBACd,0BAAc;iBACf;aACF;YACD,wBAAY;YACZ;gBACE,OAAO,EAAE,6BAAiB;gBAC1B,QAAQ,EAAE,4CAAoB;aAC/B;YACD,8BAAkB;YAClB,4BAAgB;YAChB;gBACE,QAAQ,EAAE,0BAAc;gBACxB,OAAO,EAAE,iBAAiB;aAC3B;YACD;gBACE,OAAO,EAAE,2BAA2B;gBACpC,QAAQ,EAAE,oDAAwB;aACnC;SACF;KACF,CAAC;GACW,WAAW,CAAG","sourcesContent":["// Copyright 2020-2025 SubQuery Pte Ltd authors & contributors\n// SPDX-License-Identifier: GPL-3.0\n\nimport path from 'node:path';\nimport { Module } from '@nestjs/common';\nimport { EventEmitter2 } from '@nestjs/event-emitter';\nimport { SubqlEthereumDataSource } from '@subql/common-ethereum';\nimport {\n StoreService,\n NodeConfig,\n ConnectionPoolService,\n ConnectionPoolStateManager,\n IProjectUpgradeService,\n PoiSyncService,\n InMemoryCacheService,\n MonitorService,\n CoreModule,\n IStoreModelProvider,\n ProjectService,\n DynamicDsService,\n WorkerBlockDispatcher,\n BlockDispatcher,\n DsProcessorService,\n FetchService,\n DictionaryService,\n} from '@subql/node-core';\nimport { BlockchainService } from '../blockchain.service';\nimport { SubqueryProject } from '../configure/SubqueryProject';\nimport { EthereumApiConnection } from '../ethereum/api.connection';\nimport { EthereumApiService } from '../ethereum/api.service.ethereum';\nimport { EthDictionaryService } from './dictionary/ethDictionary.service';\nimport { IndexerManager } from './indexer.manager';\nimport { UnfinalizedBlocksService } from './unfinalizedBlocks.service';\n\n@Module({\n imports: [CoreModule],\n providers: [\n {\n provide: 'APIService',\n useFactory: EthereumApiService.init,\n inject: [\n 'ISubqueryProject',\n ConnectionPoolService,\n EventEmitter2,\n NodeConfig,\n ],\n },\n {\n provide: 'IBlockchainService',\n useClass: BlockchainService,\n },\n IndexerManager,\n {\n provide: 'IBlockDispatcher',\n useFactory: (\n nodeConfig: NodeConfig,\n eventEmitter: EventEmitter2,\n projectService: ProjectService<SubqlEthereumDataSource>,\n projectUpgradeService: IProjectUpgradeService,\n cacheService: InMemoryCacheService,\n storeService: StoreService,\n storeModelProvider: IStoreModelProvider,\n poiSyncService: PoiSyncService,\n project: SubqueryProject,\n dynamicDsService: DynamicDsService<SubqlEthereumDataSource>,\n unfinalizedBlocks: UnfinalizedBlocksService,\n connectionPoolState: ConnectionPoolStateManager<EthereumApiConnection>,\n blockchainService: BlockchainService,\n indexerManager: IndexerManager,\n monitorService?: MonitorService,\n ) =>\n nodeConfig.workers\n ? new WorkerBlockDispatcher(\n nodeConfig,\n eventEmitter,\n projectService,\n projectUpgradeService,\n storeService,\n storeModelProvider,\n cacheService,\n poiSyncService,\n dynamicDsService,\n unfinalizedBlocks,\n connectionPoolState,\n project,\n blockchainService,\n path.resolve(__dirname, '../../dist/indexer/worker/worker.js'),\n [],\n monitorService,\n )\n : new BlockDispatcher(\n nodeConfig,\n eventEmitter,\n projectService,\n projectUpgradeService,\n storeService,\n storeModelProvider,\n poiSyncService,\n project,\n blockchainService,\n indexerManager,\n ),\n inject: [\n NodeConfig,\n EventEmitter2,\n 'IProjectService',\n 'IProjectUpgradeService',\n InMemoryCacheService,\n StoreService,\n 'IStoreModelProvider',\n PoiSyncService,\n 'ISubqueryProject',\n DynamicDsService,\n 'IUnfinalizedBlocksService',\n ConnectionPoolStateManager,\n 'IBlockchainService',\n IndexerManager,\n MonitorService,\n ],\n },\n FetchService,\n {\n provide: DictionaryService,\n useClass: EthDictionaryService,\n },\n DsProcessorService,\n DynamicDsService,\n {\n useClass: ProjectService,\n provide: 'IProjectService',\n },\n {\n provide: 'IUnfinalizedBlocksService',\n useClass: UnfinalizedBlocksService,\n },\n ],\n})\nexport class FetchModule {}\n"]}
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
import { isBlockHandlerProcessor, isCallHandlerProcessor, isEventHandlerProcessor, isCustomDs, isRuntimeDs, SubqlEthereumCustomDataSource, EthereumHandlerKind, EthereumRuntimeHandlerInputMap, SubqlEthereumDataSource } from '@subql/common-ethereum';
|
|
2
|
-
import { ApiService, NodeConfig, IndexerSandbox, ProcessBlockResponse, BaseIndexerManager, IBlock, SandboxService } from '@subql/node-core';
|
|
2
|
+
import { ApiService, NodeConfig, IndexerSandbox, ProcessBlockResponse, BaseIndexerManager, IBlock, SandboxService, DsProcessorService, DynamicDsService } from '@subql/node-core';
|
|
3
3
|
import { EthereumTransaction, EthereumLog, EthereumBlock, SubqlRuntimeDatasource, EthereumBlockFilter, EthereumLogFilter, EthereumTransactionFilter, LightEthereumLog } from '@subql/types-ethereum';
|
|
4
|
+
import { BlockchainService } from '../blockchain.service';
|
|
4
5
|
import { EthereumProjectDs } from '../configure/SubqueryProject';
|
|
5
6
|
import { EthereumApi } from '../ethereum';
|
|
6
7
|
import SafeEthProvider from '../ethereum/safe-api';
|
|
7
|
-
import { DsProcessorService } from './ds-processor.service';
|
|
8
|
-
import { DynamicDsService } from './dynamic-ds.service';
|
|
9
8
|
import { BlockContent } from './types';
|
|
10
9
|
import { UnfinalizedBlocksService } from './unfinalizedBlocks.service';
|
|
11
10
|
export declare class IndexerManager extends BaseIndexerManager<EthereumApi, SafeEthProvider, BlockContent, ApiService, SubqlEthereumDataSource, SubqlEthereumCustomDataSource, typeof FilterTypeMap, typeof ProcessorTypeMap, EthereumRuntimeHandlerInputMap> {
|
|
12
11
|
protected isRuntimeDs: typeof isRuntimeDs;
|
|
13
12
|
protected isCustomDs: typeof isCustomDs;
|
|
14
|
-
constructor(apiService: ApiService, nodeConfig: NodeConfig, sandboxService: SandboxService<SafeEthProvider, EthereumApi>, dsProcessorService: DsProcessorService, dynamicDsService: DynamicDsService
|
|
13
|
+
constructor(apiService: ApiService, nodeConfig: NodeConfig, sandboxService: SandboxService<SafeEthProvider, EthereumApi>, dsProcessorService: DsProcessorService<SubqlEthereumDataSource, SubqlEthereumCustomDataSource>, dynamicDsService: DynamicDsService<SubqlEthereumDataSource>, unfinalizedBlocksService: UnfinalizedBlocksService, blockchainService: BlockchainService);
|
|
15
14
|
indexBlock(block: IBlock<BlockContent>, dataSources: SubqlEthereumDataSource[]): Promise<ProcessBlockResponse>;
|
|
16
|
-
private getApi;
|
|
17
15
|
protected getDsProcessor(ds: SubqlEthereumDataSource, safeApi: SafeEthProvider): IndexerSandbox;
|
|
18
16
|
protected indexBlockData(block: BlockContent, dataSources: EthereumProjectDs[], getVM: (d: EthereumProjectDs) => Promise<IndexerSandbox>): Promise<void>;
|
|
19
17
|
private indexBlockContent;
|
|
@@ -10,27 +10,25 @@ 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 __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
14
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
15
|
+
};
|
|
13
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
17
|
exports.IndexerManager = void 0;
|
|
15
18
|
const common_1 = require("@nestjs/common");
|
|
16
19
|
const common_ethereum_1 = require("@subql/common-ethereum");
|
|
17
20
|
const node_core_1 = require("@subql/node-core");
|
|
21
|
+
const blockchain_service_1 = require("../blockchain.service");
|
|
18
22
|
const block_ethereum_1 = require("../ethereum/block.ethereum");
|
|
19
|
-
const ds_processor_service_1 = require("./ds-processor.service");
|
|
20
|
-
const dynamic_ds_service_1 = require("./dynamic-ds.service");
|
|
21
23
|
const unfinalizedBlocks_service_1 = require("./unfinalizedBlocks.service");
|
|
22
24
|
let IndexerManager = class IndexerManager extends node_core_1.BaseIndexerManager {
|
|
23
25
|
isRuntimeDs = common_ethereum_1.isRuntimeDs;
|
|
24
26
|
isCustomDs = common_ethereum_1.isCustomDs;
|
|
25
|
-
constructor(apiService, nodeConfig, sandboxService, dsProcessorService, dynamicDsService, unfinalizedBlocksService) {
|
|
26
|
-
super(apiService, nodeConfig, sandboxService, dsProcessorService, dynamicDsService, unfinalizedBlocksService, FilterTypeMap, ProcessorTypeMap);
|
|
27
|
+
constructor(apiService, nodeConfig, sandboxService, dsProcessorService, dynamicDsService, unfinalizedBlocksService, blockchainService) {
|
|
28
|
+
super(apiService, nodeConfig, sandboxService, dsProcessorService, dynamicDsService, unfinalizedBlocksService, FilterTypeMap, ProcessorTypeMap, blockchainService);
|
|
27
29
|
}
|
|
28
30
|
async indexBlock(block, dataSources) {
|
|
29
|
-
return super.internalIndexBlock(block, dataSources, () => this.
|
|
30
|
-
}
|
|
31
|
-
// eslint-disable-next-line @typescript-eslint/require-await
|
|
32
|
-
async getApi(block) {
|
|
33
|
-
return this.apiService.safeApi(block.number);
|
|
31
|
+
return super.internalIndexBlock(block, dataSources, () => this.blockchainService.getSafeApi(block.block));
|
|
34
32
|
}
|
|
35
33
|
getDsProcessor(ds, safeApi) {
|
|
36
34
|
return this.sandboxService.getDsProcessor(ds, safeApi, this.apiService.unsafeApi.api);
|
|
@@ -80,12 +78,16 @@ __decorate([
|
|
|
80
78
|
], IndexerManager.prototype, "indexBlock", null);
|
|
81
79
|
exports.IndexerManager = IndexerManager = __decorate([
|
|
82
80
|
(0, common_1.Injectable)(),
|
|
81
|
+
__param(0, (0, common_1.Inject)('APIService')),
|
|
82
|
+
__param(5, (0, common_1.Inject)('IUnfinalizedBlocksService')),
|
|
83
|
+
__param(6, (0, common_1.Inject)('IBlockchainService')),
|
|
83
84
|
__metadata("design:paramtypes", [node_core_1.ApiService,
|
|
84
85
|
node_core_1.NodeConfig,
|
|
85
86
|
node_core_1.SandboxService,
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
unfinalizedBlocks_service_1.UnfinalizedBlocksService
|
|
87
|
+
node_core_1.DsProcessorService,
|
|
88
|
+
node_core_1.DynamicDsService,
|
|
89
|
+
unfinalizedBlocks_service_1.UnfinalizedBlocksService,
|
|
90
|
+
blockchain_service_1.BlockchainService])
|
|
89
91
|
], IndexerManager);
|
|
90
92
|
const ProcessorTypeMap = {
|
|
91
93
|
[common_ethereum_1.EthereumHandlerKind.Block]: common_ethereum_1.isBlockHandlerProcessor,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"indexer.manager.js","sourceRoot":"","sources":["../../src/indexer/indexer.manager.ts"],"names":[],"mappings":";AAAA,8DAA8D;AAC9D,mCAAmC
|
|
1
|
+
{"version":3,"file":"indexer.manager.js","sourceRoot":"","sources":["../../src/indexer/indexer.manager.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2020-2025 SubQuery Pte Ltd authors & contributors\n// SPDX-License-Identifier: GPL-3.0\n\nimport { Inject, Injectable } from '@nestjs/common';\nimport {\n isBlockHandlerProcessor,\n isCallHandlerProcessor,\n isEventHandlerProcessor,\n isCustomDs,\n isRuntimeDs,\n SubqlEthereumCustomDataSource,\n EthereumHandlerKind,\n EthereumRuntimeHandlerInputMap,\n SubqlEthereumDataSource,\n} from '@subql/common-ethereum';\nimport {\n ApiService,\n NodeConfig,\n profiler,\n IndexerSandbox,\n ProcessBlockResponse,\n BaseIndexerManager,\n IBlock,\n SandboxService,\n DsProcessorService,\n DynamicDsService,\n} from '@subql/node-core';\nimport {\n EthereumTransaction,\n EthereumLog,\n EthereumBlock,\n SubqlRuntimeDatasource,\n EthereumBlockFilter,\n EthereumLogFilter,\n EthereumTransactionFilter,\n LightEthereumLog,\n} from '@subql/types-ethereum';\nimport { BlockchainService } from '../blockchain.service';\nimport { EthereumProjectDs } from '../configure/SubqueryProject';\nimport { EthereumApi } from '../ethereum';\nimport {\n filterBlocksProcessor,\n filterLogsProcessor,\n filterTransactionsProcessor,\n isFullBlock,\n} from '../ethereum/block.ethereum';\nimport SafeEthProvider from '../ethereum/safe-api';\nimport { BlockContent } from './types';\nimport { UnfinalizedBlocksService } from './unfinalizedBlocks.service';\n\n@Injectable()\nexport class IndexerManager extends BaseIndexerManager<\n EthereumApi,\n SafeEthProvider,\n BlockContent,\n ApiService,\n SubqlEthereumDataSource,\n SubqlEthereumCustomDataSource,\n typeof FilterTypeMap,\n typeof ProcessorTypeMap,\n EthereumRuntimeHandlerInputMap\n> {\n protected isRuntimeDs = isRuntimeDs;\n protected isCustomDs = isCustomDs;\n\n constructor(\n @Inject('APIService') apiService: ApiService,\n nodeConfig: NodeConfig,\n sandboxService: SandboxService<SafeEthProvider, EthereumApi>,\n dsProcessorService: DsProcessorService<\n SubqlEthereumDataSource,\n SubqlEthereumCustomDataSource\n >,\n dynamicDsService: DynamicDsService<SubqlEthereumDataSource>,\n @Inject('IUnfinalizedBlocksService')\n unfinalizedBlocksService: UnfinalizedBlocksService,\n @Inject('IBlockchainService') blockchainService: BlockchainService,\n ) {\n super(\n apiService,\n nodeConfig,\n sandboxService,\n dsProcessorService,\n dynamicDsService,\n unfinalizedBlocksService,\n FilterTypeMap,\n ProcessorTypeMap,\n blockchainService,\n );\n }\n\n @profiler()\n async indexBlock(\n block: IBlock<BlockContent>,\n dataSources: SubqlEthereumDataSource[],\n ): Promise<ProcessBlockResponse> {\n return super.internalIndexBlock(block, dataSources, () =>\n this.blockchainService.getSafeApi(block.block),\n );\n }\n\n protected getDsProcessor(\n ds: SubqlEthereumDataSource,\n safeApi: SafeEthProvider,\n ): IndexerSandbox {\n return this.sandboxService.getDsProcessor(\n ds,\n safeApi,\n this.apiService.unsafeApi.api,\n );\n }\n\n protected async indexBlockData(\n block: BlockContent,\n dataSources: EthereumProjectDs[],\n getVM: (d: EthereumProjectDs) => Promise<IndexerSandbox>,\n ): Promise<void> {\n if (isFullBlock(block)) {\n await this.indexBlockContent(block, dataSources, getVM);\n\n for (const tx of block.transactions) {\n await this.indexTransaction(tx, dataSources, getVM);\n\n for (const log of tx.logs ?? []) {\n await this.indexEvent(log, dataSources, getVM);\n }\n }\n } else {\n for (const log of block.logs ?? []) {\n await this.indexEvent(log, dataSources, getVM);\n }\n }\n }\n\n private async indexBlockContent(\n block: EthereumBlock,\n dataSources: EthereumProjectDs[],\n getVM: (d: EthereumProjectDs) => Promise<IndexerSandbox>,\n ): Promise<void> {\n for (const ds of dataSources) {\n await this.indexData(EthereumHandlerKind.Block, block, ds, getVM);\n }\n }\n\n private async indexTransaction(\n tx: EthereumTransaction,\n dataSources: EthereumProjectDs[],\n getVM: (d: EthereumProjectDs) => Promise<IndexerSandbox>,\n ): Promise<void> {\n for (const ds of dataSources) {\n await this.indexData(EthereumHandlerKind.Call, tx, ds, getVM);\n }\n }\n\n private async indexEvent(\n log: EthereumLog | LightEthereumLog,\n dataSources: EthereumProjectDs[],\n getVM: (d: EthereumProjectDs) => Promise<IndexerSandbox>,\n ): Promise<void> {\n for (const ds of dataSources) {\n await this.indexData(EthereumHandlerKind.Event, log, ds, getVM);\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/require-await\n protected async prepareFilteredData(\n kind: EthereumHandlerKind,\n data: any,\n ds: SubqlRuntimeDatasource,\n ): Promise<any> {\n return DataAbiParser[kind](this.apiService.api)(data, ds);\n }\n}\n\nconst ProcessorTypeMap = {\n [EthereumHandlerKind.Block]: isBlockHandlerProcessor,\n [EthereumHandlerKind.Event]: isEventHandlerProcessor,\n [EthereumHandlerKind.Call]: isCallHandlerProcessor,\n};\n\nconst FilterTypeMap = {\n [EthereumHandlerKind.Block]: (\n data: EthereumBlock,\n filter: EthereumBlockFilter,\n ds: SubqlEthereumDataSource,\n ) => filterBlocksProcessor(data, filter, ds.options?.address),\n [EthereumHandlerKind.Event]: (\n data: EthereumLog | LightEthereumLog,\n filter: EthereumLogFilter,\n ds: SubqlEthereumDataSource,\n ) => filterLogsProcessor(data, filter, ds.options?.address),\n [EthereumHandlerKind.Call]: (\n data: EthereumTransaction,\n filter: EthereumTransactionFilter,\n ds: SubqlEthereumDataSource,\n ) => filterTransactionsProcessor(data, filter, ds.options?.address),\n};\n\nconst DataAbiParser = {\n [EthereumHandlerKind.Block]: () => (data: EthereumBlock) => data,\n [EthereumHandlerKind.Event]:\n (api: EthereumApi) =>\n (data: EthereumLog | LightEthereumLog, ds: SubqlRuntimeDatasource) =>\n api.parseLog(data, ds),\n [EthereumHandlerKind.Call]:\n (api: EthereumApi) =>\n (data: EthereumTransaction, ds: SubqlRuntimeDatasource) =>\n api.parseTransaction(data, ds),\n};\n"]}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { UnfinalizedBlocksService as BaseUnfinalizedBlocksService, Header, NodeConfig, IStoreModelProvider } from '@subql/node-core';
|
|
2
|
+
import { BlockchainService } from '../blockchain.service';
|
|
2
3
|
import { BlockContent } from './types';
|
|
3
4
|
export declare class UnfinalizedBlocksService extends BaseUnfinalizedBlocksService<BlockContent> {
|
|
4
|
-
private readonly apiService;
|
|
5
5
|
private supportsFinalization?;
|
|
6
6
|
private startupCheck;
|
|
7
|
-
constructor(
|
|
7
|
+
constructor(nodeConfig: NodeConfig, storeModelProvider: IStoreModelProvider, blockchainService: BlockchainService);
|
|
8
8
|
/**
|
|
9
9
|
* @param reindex - the function to reindex back before a fork
|
|
10
10
|
* @param supportsFinalization - If the chain supports the 'finalized' block tag this should be true.
|
|
@@ -20,7 +20,4 @@ export declare class UnfinalizedBlocksService extends BaseUnfinalizedBlocksServi
|
|
|
20
20
|
* @return (number | undefined) - The block height to rewind to to remove forked data
|
|
21
21
|
**/
|
|
22
22
|
protected getLastCorrectFinalizedBlock(forkedHeader: Header): Promise<Header | undefined>;
|
|
23
|
-
protected getFinalizedHead(): Promise<Header>;
|
|
24
|
-
protected getHeaderForHash(hash: string): Promise<Header>;
|
|
25
|
-
getHeaderForHeight(height: number): Promise<Header>;
|
|
26
23
|
}
|
|
@@ -18,16 +18,15 @@ exports.UnfinalizedBlocksService = void 0;
|
|
|
18
18
|
const common_1 = require("@nestjs/common");
|
|
19
19
|
const node_core_1 = require("@subql/node-core");
|
|
20
20
|
const lodash_1 = require("lodash");
|
|
21
|
+
const blockchain_service_1 = require("../blockchain.service");
|
|
21
22
|
const NodeConfig_1 = require("../configure/NodeConfig");
|
|
22
|
-
const utils_ethereum_1 = require("../ethereum/utils.ethereum");
|
|
23
23
|
const logger = (0, node_core_1.getLogger)('UnfinalizedBlocksService');
|
|
24
|
-
let UnfinalizedBlocksService = class UnfinalizedBlocksService extends node_core_1.
|
|
25
|
-
apiService;
|
|
24
|
+
let UnfinalizedBlocksService = class UnfinalizedBlocksService extends node_core_1.UnfinalizedBlocksService {
|
|
26
25
|
supportsFinalization;
|
|
27
26
|
startupCheck = true;
|
|
28
|
-
constructor(
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
constructor(nodeConfig, storeModelProvider, blockchainService) {
|
|
28
|
+
// blockchain service cast is due to unsolvable typescript generic error, it wokrs on the main sdk but not here
|
|
29
|
+
super(new NodeConfig_1.EthereumNodeConfig(nodeConfig), storeModelProvider, blockchainService);
|
|
31
30
|
}
|
|
32
31
|
/**
|
|
33
32
|
* @param reindex - the function to reindex back before a fork
|
|
@@ -51,7 +50,7 @@ let UnfinalizedBlocksService = class UnfinalizedBlocksService extends node_core_
|
|
|
51
50
|
this.startupCheck = false;
|
|
52
51
|
const lastUnfinalized = (0, lodash_1.last)(this.unfinalizedBlocks);
|
|
53
52
|
if (lastUnfinalized) {
|
|
54
|
-
const checkUnfinalized = await this.getHeaderForHeight(lastUnfinalized.blockHeight);
|
|
53
|
+
const checkUnfinalized = await this.blockchainService.getHeaderForHeight(lastUnfinalized.blockHeight);
|
|
55
54
|
if (lastUnfinalized.blockHash !== checkUnfinalized.blockHash) {
|
|
56
55
|
return checkUnfinalized;
|
|
57
56
|
}
|
|
@@ -90,7 +89,7 @@ let UnfinalizedBlocksService = class UnfinalizedBlocksService extends node_core_
|
|
|
90
89
|
if (!checkingHeader.parentHash) {
|
|
91
90
|
throw new Error('Unable to get parent hash for header');
|
|
92
91
|
}
|
|
93
|
-
checkingHeader = await this.getHeaderForHash(checkingHeader.parentHash);
|
|
92
|
+
checkingHeader = await this.blockchainService.getHeaderForHash(checkingHeader.parentHash);
|
|
94
93
|
}
|
|
95
94
|
try {
|
|
96
95
|
const poiHeader = await this.findFinalizedUsingPOI(checkingHeader);
|
|
@@ -108,18 +107,6 @@ let UnfinalizedBlocksService = class UnfinalizedBlocksService extends node_core_
|
|
|
108
107
|
throw e;
|
|
109
108
|
}
|
|
110
109
|
}
|
|
111
|
-
async getFinalizedHead() {
|
|
112
|
-
const finalizedBlock = await this.apiService.api.getFinalizedBlock();
|
|
113
|
-
return (0, utils_ethereum_1.ethereumBlockToHeader)(finalizedBlock);
|
|
114
|
-
}
|
|
115
|
-
async getHeaderForHash(hash) {
|
|
116
|
-
const block = await this.apiService.api.getBlockByHeightOrHash(hash);
|
|
117
|
-
return (0, utils_ethereum_1.ethereumBlockToHeader)(block);
|
|
118
|
-
}
|
|
119
|
-
async getHeaderForHeight(height) {
|
|
120
|
-
const block = await this.apiService.api.getBlockByHeightOrHash(height);
|
|
121
|
-
return (0, utils_ethereum_1.ethereumBlockToHeader)(block);
|
|
122
|
-
}
|
|
123
110
|
};
|
|
124
111
|
exports.UnfinalizedBlocksService = UnfinalizedBlocksService;
|
|
125
112
|
__decorate([
|
|
@@ -128,28 +115,10 @@ __decorate([
|
|
|
128
115
|
__metadata("design:paramtypes", []),
|
|
129
116
|
__metadata("design:returntype", Promise)
|
|
130
117
|
], UnfinalizedBlocksService.prototype, "hasForked", null);
|
|
131
|
-
__decorate([
|
|
132
|
-
(0, node_core_1.mainThreadOnly)(),
|
|
133
|
-
__metadata("design:type", Function),
|
|
134
|
-
__metadata("design:paramtypes", []),
|
|
135
|
-
__metadata("design:returntype", Promise)
|
|
136
|
-
], UnfinalizedBlocksService.prototype, "getFinalizedHead", null);
|
|
137
|
-
__decorate([
|
|
138
|
-
(0, node_core_1.mainThreadOnly)(),
|
|
139
|
-
__metadata("design:type", Function),
|
|
140
|
-
__metadata("design:paramtypes", [String]),
|
|
141
|
-
__metadata("design:returntype", Promise)
|
|
142
|
-
], UnfinalizedBlocksService.prototype, "getHeaderForHash", null);
|
|
143
|
-
__decorate([
|
|
144
|
-
(0, node_core_1.mainThreadOnly)(),
|
|
145
|
-
__metadata("design:type", Function),
|
|
146
|
-
__metadata("design:paramtypes", [Number]),
|
|
147
|
-
__metadata("design:returntype", Promise)
|
|
148
|
-
], UnfinalizedBlocksService.prototype, "getHeaderForHeight", null);
|
|
149
118
|
exports.UnfinalizedBlocksService = UnfinalizedBlocksService = __decorate([
|
|
150
119
|
(0, common_1.Injectable)(),
|
|
151
|
-
__param(
|
|
152
|
-
|
|
153
|
-
|
|
120
|
+
__param(1, (0, common_1.Inject)('IStoreModelProvider')),
|
|
121
|
+
__param(2, (0, common_1.Inject)('IBlockchainService')),
|
|
122
|
+
__metadata("design:paramtypes", [node_core_1.NodeConfig, Object, blockchain_service_1.BlockchainService])
|
|
154
123
|
], UnfinalizedBlocksService);
|
|
155
124
|
//# sourceMappingURL=unfinalizedBlocks.service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unfinalizedBlocks.service.js","sourceRoot":"","sources":["../../src/indexer/unfinalizedBlocks.service.ts"],"names":[],"mappings":";AAAA,8DAA8D;AAC9D,mCAAmC;;;;;;;;;;;;;;;AAEnC,2CAAoD;AACpD,
|
|
1
|
+
{"version":3,"file":"unfinalizedBlocks.service.js","sourceRoot":"","sources":["../../src/indexer/unfinalizedBlocks.service.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2020-2025 SubQuery Pte Ltd authors & contributors\n// SPDX-License-Identifier: GPL-3.0\n\nimport { Inject, Injectable } from '@nestjs/common';\nimport {\n UnfinalizedBlocksService as BaseUnfinalizedBlocksService,\n Header,\n NodeConfig,\n getLogger,\n profiler,\n POI_NOT_ENABLED_ERROR_MESSAGE,\n IStoreModelProvider,\n IBlockchainService,\n} from '@subql/node-core';\nimport { last } from 'lodash';\nimport { BlockchainService } from '../blockchain.service';\nimport { EthereumNodeConfig } from '../configure/NodeConfig';\nimport { BlockContent } from './types';\n\nconst logger = getLogger('UnfinalizedBlocksService');\n\n@Injectable()\nexport class UnfinalizedBlocksService extends BaseUnfinalizedBlocksService<BlockContent> {\n private supportsFinalization?: boolean;\n private startupCheck = true;\n\n constructor(\n nodeConfig: NodeConfig,\n @Inject('IStoreModelProvider') storeModelProvider: IStoreModelProvider,\n @Inject('IBlockchainService') blockchainService: BlockchainService,\n ) {\n // blockchain service cast is due to unsolvable typescript generic error, it wokrs on the main sdk but not here\n super(\n new EthereumNodeConfig(nodeConfig),\n storeModelProvider,\n blockchainService as IBlockchainService,\n );\n }\n\n /**\n * @param reindex - the function to reindex back before a fork\n * @param supportsFinalization - If the chain supports the 'finalized' block tag this should be true.\n * */\n // eslint-disable-next-line @typescript-eslint/require-await\n async init(\n reindex: (targetHeight: Header) => Promise<void>,\n supportsFinalisation?: boolean,\n ): Promise<Header | undefined> {\n this.supportsFinalization = supportsFinalisation;\n return super.init(reindex);\n }\n /**\n * Checks if a fork has happened, this doesn't find the start of the fork just where it was detected\n * @returns (Header | undefined) - The header may be the forked header but will most likely be the main header. Either way it should be used just for the block height\n * */\n @profiler()\n protected async hasForked(): Promise<Header | undefined> {\n if (this.supportsFinalization) {\n return super.hasForked();\n }\n\n // Startup check helps speed up finding a fork by checking the hash of the last unfinalized block\n if (this.startupCheck) {\n this.startupCheck = false;\n const lastUnfinalized = last(this.unfinalizedBlocks);\n if (lastUnfinalized) {\n const checkUnfinalized =\n await this.blockchainService.getHeaderForHeight(\n lastUnfinalized.blockHeight,\n );\n\n if (lastUnfinalized.blockHash !== checkUnfinalized.blockHash) {\n return checkUnfinalized;\n }\n }\n }\n\n if (this.unfinalizedBlocks.length <= 2) {\n return;\n }\n\n const i = this.unfinalizedBlocks.length - 1;\n const current = this.unfinalizedBlocks[i];\n const parent = this.unfinalizedBlocks[i - 1];\n\n if (current.parentHash !== parent.blockHash) {\n // We've found a fork now we need to find where the fork happened\n logger.warn(\n `Block fork detected at ${current.blockHeight}. Parent hash ${current.parentHash} doesn't match indexed parent ${parent.blockHash}.`,\n );\n\n return current;\n }\n\n return;\n }\n\n /**\n * Finds the height before the fork occurred based on the result of hasForked\n * @return (number | undefined) - The block height to rewind to to remove forked data\n **/\n protected async getLastCorrectFinalizedBlock(\n forkedHeader: Header,\n ): Promise<Header | undefined> {\n if (this.supportsFinalization) {\n return super.getLastCorrectFinalizedBlock(forkedHeader);\n }\n\n const bestVerifiableBlocks = this.unfinalizedBlocks.filter(\n ({ blockHeight }) => blockHeight < forkedHeader.blockHeight,\n );\n\n let checkingHeader = forkedHeader;\n\n // Work backwards through the blocks until we find a matching hash\n for (const header of bestVerifiableBlocks.reverse()) {\n if (\n header.blockHash === checkingHeader.blockHash ||\n header.blockHash === checkingHeader.parentHash\n ) {\n return header;\n }\n\n // Get the new parent\n if (!checkingHeader.parentHash) {\n throw new Error('Unable to get parent hash for header');\n }\n checkingHeader = await this.blockchainService.getHeaderForHash(\n checkingHeader.parentHash,\n );\n }\n\n try {\n const poiHeader = await this.findFinalizedUsingPOI(checkingHeader);\n return poiHeader;\n } catch (e: any) {\n if (e.message === POI_NOT_ENABLED_ERROR_MESSAGE) {\n return {\n blockHeight: Math.max(\n 0,\n forkedHeader.blockHeight -\n (this.nodeConfig as EthereumNodeConfig).blockForkReindex,\n ),\n } as Header;\n }\n // TODO rewind back 1000+ blocks\n logger.info('Failed to use POI to rewind block');\n throw e;\n }\n }\n}\n"]}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
5
|
const bytes_1 = require("@ethersproject/bytes");
|
|
6
6
|
const node_core_1 = require("@subql/node-core");
|
|
7
|
+
const blockchain_service_1 = require("../blockchain.service");
|
|
7
8
|
const unfinalizedBlocks_service_1 = require("./unfinalizedBlocks.service");
|
|
8
9
|
// Adds 0 padding so we can convert to POI block
|
|
9
10
|
const hexify = (input) => (0, bytes_1.hexZeroPad)(input, 4);
|
|
@@ -67,10 +68,11 @@ describe('UnfinalizedBlockService', () => {
|
|
|
67
68
|
let storeCache;
|
|
68
69
|
beforeEach(() => {
|
|
69
70
|
storeCache = mockStoreCache();
|
|
70
|
-
|
|
71
|
+
const blockchainService = new blockchain_service_1.BlockchainService(getMockApi());
|
|
72
|
+
unfinalizedBlocks = new unfinalizedBlocks_service_1.UnfinalizedBlocksService(new node_core_1.NodeConfig({
|
|
71
73
|
unfinalizedBlocks: true,
|
|
72
74
|
blockForkReindex: 1000,
|
|
73
|
-
}), storeCache);
|
|
75
|
+
}), storeCache, blockchainService);
|
|
74
76
|
});
|
|
75
77
|
it('handles a block fork', async () => {
|
|
76
78
|
await unfinalizedBlocks.init(jest.fn());
|
|
@@ -112,12 +114,12 @@ describe('UnfinalizedBlockService', () => {
|
|
|
112
114
|
});
|
|
113
115
|
// The finalized block is after the cached unfinalized blocks, they should be rechecked
|
|
114
116
|
it('startup, correctly checks for forks after cached unfinalized blocks', async () => {
|
|
115
|
-
storeCache.metadata.set(node_core_1.METADATA_UNFINALIZED_BLOCKS_KEY, JSON.stringify([
|
|
117
|
+
await storeCache.metadata.set(node_core_1.METADATA_UNFINALIZED_BLOCKS_KEY, JSON.stringify([
|
|
116
118
|
makeHeader(99, true),
|
|
117
119
|
makeHeader(100),
|
|
118
120
|
makeHeader(101),
|
|
119
121
|
]));
|
|
120
|
-
storeCache.metadata.set(node_core_1.METADATA_LAST_FINALIZED_PROCESSED_KEY, 99);
|
|
122
|
+
await storeCache.metadata.set(node_core_1.METADATA_LAST_FINALIZED_PROCESSED_KEY, 99);
|
|
121
123
|
const rewind = jest.fn();
|
|
122
124
|
await unfinalizedBlocks.init(rewind);
|
|
123
125
|
// It should fall back to poi in this case
|
|
@@ -129,12 +131,12 @@ describe('UnfinalizedBlockService', () => {
|
|
|
129
131
|
});
|
|
130
132
|
});
|
|
131
133
|
it('startup, correctly checks for forks within cached unfinalized blocks', async () => {
|
|
132
|
-
storeCache.metadata.set(node_core_1.METADATA_UNFINALIZED_BLOCKS_KEY, JSON.stringify([
|
|
134
|
+
await storeCache.metadata.set(node_core_1.METADATA_UNFINALIZED_BLOCKS_KEY, JSON.stringify([
|
|
133
135
|
makeHeader(110),
|
|
134
136
|
makeHeader(111),
|
|
135
137
|
makeHeader(112),
|
|
136
138
|
]));
|
|
137
|
-
storeCache.metadata.set(node_core_1.METADATA_LAST_FINALIZED_PROCESSED_KEY, 109);
|
|
139
|
+
await storeCache.metadata.set(node_core_1.METADATA_LAST_FINALIZED_PROCESSED_KEY, 109);
|
|
138
140
|
const rewind = jest.fn();
|
|
139
141
|
await unfinalizedBlocks.init(rewind);
|
|
140
142
|
// It should fall back to poi in this case
|
|
@@ -146,18 +148,18 @@ describe('UnfinalizedBlockService', () => {
|
|
|
146
148
|
});
|
|
147
149
|
});
|
|
148
150
|
it('doesnt throw if there are no unfinalized blocks on startup', async () => {
|
|
149
|
-
storeCache.metadata.set(node_core_1.METADATA_LAST_FINALIZED_PROCESSED_KEY, 109);
|
|
151
|
+
await storeCache.metadata.set(node_core_1.METADATA_LAST_FINALIZED_PROCESSED_KEY, 109);
|
|
150
152
|
await expect(unfinalizedBlocks.init(jest.fn())).resolves.not.toThrow();
|
|
151
153
|
});
|
|
152
154
|
it('rewinds using blockForkReindex value if poi is not enabled', async () => {
|
|
153
155
|
// Do this to "disable" poi
|
|
154
156
|
storeCache.poi = null;
|
|
155
|
-
storeCache.metadata.set(node_core_1.METADATA_UNFINALIZED_BLOCKS_KEY, JSON.stringify([
|
|
157
|
+
await storeCache.metadata.set(node_core_1.METADATA_UNFINALIZED_BLOCKS_KEY, JSON.stringify([
|
|
156
158
|
makeHeader(110),
|
|
157
159
|
makeHeader(111),
|
|
158
160
|
makeHeader(112),
|
|
159
161
|
]));
|
|
160
|
-
storeCache.metadata.set(node_core_1.METADATA_LAST_FINALIZED_PROCESSED_KEY, 109);
|
|
162
|
+
await storeCache.metadata.set(node_core_1.METADATA_LAST_FINALIZED_PROCESSED_KEY, 109);
|
|
161
163
|
const rewind = jest.fn();
|
|
162
164
|
await unfinalizedBlocks.init(rewind);
|
|
163
165
|
// It should fall back to poi in this case
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unfinalizedBlocks.service.spec.js","sourceRoot":"","sources":["../../src/indexer/unfinalizedBlocks.service.spec.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2020-2025 SubQuery Pte Ltd authors & contributors\n// SPDX-License-Identifier: GPL-3.0\n\nimport { hexZeroPad } from '@ethersproject/bytes';\nimport {\n ApiService,\n CacheMetadataModel,\n Header,\n NodeConfig,\n PoiBlock,\n StoreCacheService,\n METADATA_UNFINALIZED_BLOCKS_KEY,\n METADATA_LAST_FINALIZED_PROCESSED_KEY,\n} from '@subql/node-core';\nimport { EthereumNodeConfig } from '../configure/NodeConfig';\nimport { UnfinalizedBlocksService } from './unfinalizedBlocks.service';\n\n// Adds 0 padding so we can convert to POI block\nconst hexify = (input: string) => hexZeroPad(input, 4);\n\n// Mock 1740100000 is the timestamp of the genesis block\nconst genBlockTimestamp = (height: number) => (1740100000 + height) * 1000;\nconst genBlockDate = (height: number) => new Date(genBlockTimestamp(height));\n\nconst makeHeader = (height: number, finalized?: boolean): Header => ({\n blockHeight: height,\n blockHash: hexify(`0xABC${height}${finalized ? 'f' : ''}`),\n parentHash: hexify(`0xABC${height - 1}${finalized ? 'f' : ''}`),\n timestamp: genBlockDate(height),\n});\n\nconst getMockApi = (): ApiService => {\n return {\n api: {\n getBlockByHeightOrHash: (hash: string | number) => {\n const num =\n typeof hash === 'number'\n ? hash\n : Number(\n hash\n .toString()\n .replace('0x', '')\n .replace('ABC', '')\n .replace('f', ''),\n );\n return Promise.resolve({\n number: num,\n hash: typeof hash === 'number' ? hexify(`0xABC${hash}f`) : hash,\n parentHash: hexify(`0xABC${num - 1}f`),\n timestamp: genBlockTimestamp(num) / 1000,\n });\n },\n getFinalizedBlock: jest.fn(() => ({\n number: 110,\n hash: '0xABC110f',\n parentHash: '0xABC109f',\n timestamp: genBlockTimestamp(110) / 1000,\n })),\n },\n } as any;\n};\n\nfunction getMockMetadata(): any {\n const data: Record<string, any> = {};\n return {\n upsert: ({ key, value }: any) => (data[key] = value),\n findOne: ({ where: { key } }: any) => ({ value: data[key] }),\n findByPk: (key: string) => data[key],\n find: (key: string) => data[key],\n } as any;\n}\n\nfunction mockStoreCache(): StoreCacheService {\n return {\n metadata: new CacheMetadataModel(getMockMetadata()),\n poi: {\n getPoiBlocksBefore: jest.fn(() => [\n PoiBlock.create(99, hexify('0xABC99f'), new Uint8Array(), ''),\n ]),\n },\n } as any as StoreCacheService;\n}\n\ndescribe('UnfinalizedBlockService', () => {\n let unfinalizedBlocks: UnfinalizedBlocksService;\n let storeCache: StoreCacheService;\n\n beforeEach(() => {\n storeCache = mockStoreCache();\n\n unfinalizedBlocks = new UnfinalizedBlocksService(\n getMockApi(),\n new NodeConfig({\n unfinalizedBlocks: true,\n blockForkReindex: 1000,\n } as any) as EthereumNodeConfig,\n storeCache,\n );\n });\n\n it('handles a block fork', async () => {\n await unfinalizedBlocks.init(jest.fn());\n\n (unfinalizedBlocks as any)._unfinalizedBlocks = [\n makeHeader(100),\n makeHeader(101),\n makeHeader(102),\n makeHeader(103, true), // Where the fork started\n makeHeader(104),\n makeHeader(105),\n makeHeader(106),\n makeHeader(107),\n makeHeader(108),\n makeHeader(109),\n makeHeader(110),\n ];\n\n const rewind = await unfinalizedBlocks.processUnfinalizedBlockHeader(\n makeHeader(111, true),\n );\n\n expect(rewind?.blockHeight).toEqual(103);\n });\n\n it('uses POI blocks if there are not enough cached unfinalized blocks', async () => {\n await unfinalizedBlocks.init(jest.fn());\n\n (unfinalizedBlocks as any)._unfinalizedBlocks = [\n makeHeader(100),\n makeHeader(101),\n makeHeader(102),\n makeHeader(103),\n makeHeader(104),\n makeHeader(105),\n makeHeader(106),\n makeHeader(107),\n makeHeader(108),\n makeHeader(109),\n makeHeader(110),\n ];\n\n const spy = jest.spyOn(storeCache.poi as any, 'getPoiBlocksBefore');\n\n const rewind = await unfinalizedBlocks.processUnfinalizedBlockHeader(\n makeHeader(111, true),\n );\n\n expect(rewind?.blockHeight).toEqual(99);\n expect(spy).toHaveBeenCalled();\n });\n\n // The finalized block is after the cached unfinalized blocks, they should be rechecked\n it('startup, correctly checks for forks after cached unfinalized blocks', async () => {\n storeCache.metadata.set(\n METADATA_UNFINALIZED_BLOCKS_KEY,\n JSON.stringify(<Header[]>[\n makeHeader(99, true),\n makeHeader(100),\n makeHeader(101),\n ]),\n );\n\n storeCache.metadata.set(METADATA_LAST_FINALIZED_PROCESSED_KEY, 99);\n\n const rewind = jest.fn();\n\n await unfinalizedBlocks.init(rewind);\n\n // It should fall back to poi in this case\n expect(rewind).toHaveBeenCalledWith({\n blockHash: '0x00ABC99f',\n blockHeight: 99,\n parentHash: '0x00ABC98f',\n timestamp: genBlockDate(99),\n });\n });\n\n it('startup, correctly checks for forks within cached unfinalized blocks', async () => {\n storeCache.metadata.set(\n METADATA_UNFINALIZED_BLOCKS_KEY,\n JSON.stringify(<Header[]>[\n makeHeader(110),\n makeHeader(111),\n makeHeader(112),\n ]),\n );\n\n storeCache.metadata.set(METADATA_LAST_FINALIZED_PROCESSED_KEY, 109);\n\n const rewind = jest.fn();\n\n await unfinalizedBlocks.init(rewind);\n\n // It should fall back to poi in this case\n expect(rewind).toHaveBeenCalledWith({\n blockHash: '0x00ABC99f',\n blockHeight: 99,\n parentHash: '0x00ABC98f',\n timestamp: genBlockDate(99),\n });\n });\n\n it('doesnt throw if there are no unfinalized blocks on startup', async () => {\n storeCache.metadata.set(METADATA_LAST_FINALIZED_PROCESSED_KEY, 109);\n\n await expect(unfinalizedBlocks.init(jest.fn())).resolves.not.toThrow();\n });\n\n it('rewinds using blockForkReindex value if poi is not enabled', async () => {\n // Do this to \"disable\" poi\n (storeCache as any).poi = null;\n\n storeCache.metadata.set(\n METADATA_UNFINALIZED_BLOCKS_KEY,\n JSON.stringify(<Header[]>[\n makeHeader(110),\n makeHeader(111),\n makeHeader(112),\n ]),\n );\n\n storeCache.metadata.set(METADATA_LAST_FINALIZED_PROCESSED_KEY, 109);\n\n const rewind = jest.fn();\n\n await unfinalizedBlocks.init(rewind);\n\n // It should fall back to poi in this case\n expect(rewind).toHaveBeenCalledWith({ blockHeight: 0 });\n });\n});\n"]}
|
|
1
|
+
{"version":3,"file":"unfinalizedBlocks.service.spec.js","sourceRoot":"","sources":["../../src/indexer/unfinalizedBlocks.service.spec.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2020-2025 SubQuery Pte Ltd authors & contributors\n// SPDX-License-Identifier: GPL-3.0\n\nimport { hexZeroPad } from '@ethersproject/bytes';\nimport {\n CacheMetadataModel,\n Header,\n NodeConfig,\n PoiBlock,\n StoreCacheService,\n METADATA_UNFINALIZED_BLOCKS_KEY,\n METADATA_LAST_FINALIZED_PROCESSED_KEY,\n} from '@subql/node-core';\nimport { BlockchainService } from '../blockchain.service';\nimport { EthereumNodeConfig } from '../configure/NodeConfig';\nimport { EthereumApiService } from '../ethereum';\nimport { UnfinalizedBlocksService } from './unfinalizedBlocks.service';\n\n// Adds 0 padding so we can convert to POI block\nconst hexify = (input: string) => hexZeroPad(input, 4);\n\n// Mock 1740100000 is the timestamp of the genesis block\nconst genBlockTimestamp = (height: number) => (1740100000 + height) * 1000;\nconst genBlockDate = (height: number) => new Date(genBlockTimestamp(height));\n\nconst makeHeader = (height: number, finalized?: boolean): Header => ({\n blockHeight: height,\n blockHash: hexify(`0xABC${height}${finalized ? 'f' : ''}`),\n parentHash: hexify(`0xABC${height - 1}${finalized ? 'f' : ''}`),\n timestamp: genBlockDate(height),\n});\n\nconst getMockApi = (): EthereumApiService => {\n return {\n api: {\n getBlockByHeightOrHash: (hash: string | number) => {\n const num =\n typeof hash === 'number'\n ? hash\n : Number(\n hash\n .toString()\n .replace('0x', '')\n .replace('ABC', '')\n .replace('f', ''),\n );\n return Promise.resolve({\n number: num,\n hash: typeof hash === 'number' ? hexify(`0xABC${hash}f`) : hash,\n parentHash: hexify(`0xABC${num - 1}f`),\n timestamp: genBlockTimestamp(num) / 1000,\n });\n },\n getFinalizedBlock: jest.fn(() => ({\n number: 110,\n hash: '0xABC110f',\n parentHash: '0xABC109f',\n timestamp: genBlockTimestamp(110) / 1000,\n })),\n },\n } as any;\n};\n\nfunction getMockMetadata(): any {\n const data: Record<string, any> = {};\n return {\n upsert: ({ key, value }: any) => (data[key] = value),\n findOne: ({ where: { key } }: any) => ({ value: data[key] }),\n findByPk: (key: string) => data[key],\n find: (key: string) => data[key],\n } as any;\n}\n\nfunction mockStoreCache(): StoreCacheService {\n return {\n metadata: new CacheMetadataModel(getMockMetadata()),\n poi: {\n getPoiBlocksBefore: jest.fn(() => [\n PoiBlock.create(99, hexify('0xABC99f'), new Uint8Array(), ''),\n ]),\n },\n } as any as StoreCacheService;\n}\n\ndescribe('UnfinalizedBlockService', () => {\n let unfinalizedBlocks: UnfinalizedBlocksService;\n let storeCache: StoreCacheService;\n\n beforeEach(() => {\n storeCache = mockStoreCache();\n\n const blockchainService = new BlockchainService(getMockApi());\n\n unfinalizedBlocks = new UnfinalizedBlocksService(\n new NodeConfig({\n unfinalizedBlocks: true,\n blockForkReindex: 1000,\n } as any) as EthereumNodeConfig,\n storeCache,\n blockchainService,\n );\n });\n\n it('handles a block fork', async () => {\n await unfinalizedBlocks.init(jest.fn());\n\n (unfinalizedBlocks as any)._unfinalizedBlocks = [\n makeHeader(100),\n makeHeader(101),\n makeHeader(102),\n makeHeader(103, true), // Where the fork started\n makeHeader(104),\n makeHeader(105),\n makeHeader(106),\n makeHeader(107),\n makeHeader(108),\n makeHeader(109),\n makeHeader(110),\n ];\n\n const rewind = await unfinalizedBlocks.processUnfinalizedBlockHeader(\n makeHeader(111, true),\n );\n\n expect(rewind?.blockHeight).toEqual(103);\n });\n\n it('uses POI blocks if there are not enough cached unfinalized blocks', async () => {\n await unfinalizedBlocks.init(jest.fn());\n\n (unfinalizedBlocks as any)._unfinalizedBlocks = [\n makeHeader(100),\n makeHeader(101),\n makeHeader(102),\n makeHeader(103),\n makeHeader(104),\n makeHeader(105),\n makeHeader(106),\n makeHeader(107),\n makeHeader(108),\n makeHeader(109),\n makeHeader(110),\n ];\n\n const spy = jest.spyOn(storeCache.poi as any, 'getPoiBlocksBefore');\n\n const rewind = await unfinalizedBlocks.processUnfinalizedBlockHeader(\n makeHeader(111, true),\n );\n\n expect(rewind?.blockHeight).toEqual(99);\n expect(spy).toHaveBeenCalled();\n });\n\n // The finalized block is after the cached unfinalized blocks, they should be rechecked\n it('startup, correctly checks for forks after cached unfinalized blocks', async () => {\n await storeCache.metadata.set(\n METADATA_UNFINALIZED_BLOCKS_KEY,\n JSON.stringify(<Header[]>[\n makeHeader(99, true),\n makeHeader(100),\n makeHeader(101),\n ]),\n );\n\n await storeCache.metadata.set(METADATA_LAST_FINALIZED_PROCESSED_KEY, 99);\n\n const rewind = jest.fn();\n\n await unfinalizedBlocks.init(rewind);\n\n // It should fall back to poi in this case\n expect(rewind).toHaveBeenCalledWith({\n blockHash: '0x00ABC99f',\n blockHeight: 99,\n parentHash: '0x00ABC98f',\n timestamp: genBlockDate(99),\n });\n });\n\n it('startup, correctly checks for forks within cached unfinalized blocks', async () => {\n await storeCache.metadata.set(\n METADATA_UNFINALIZED_BLOCKS_KEY,\n JSON.stringify(<Header[]>[\n makeHeader(110),\n makeHeader(111),\n makeHeader(112),\n ]),\n );\n\n await storeCache.metadata.set(METADATA_LAST_FINALIZED_PROCESSED_KEY, 109);\n\n const rewind = jest.fn();\n\n await unfinalizedBlocks.init(rewind);\n\n // It should fall back to poi in this case\n expect(rewind).toHaveBeenCalledWith({\n blockHash: '0x00ABC99f',\n blockHeight: 99,\n parentHash: '0x00ABC98f',\n timestamp: genBlockDate(99),\n });\n });\n\n it('doesnt throw if there are no unfinalized blocks on startup', async () => {\n await storeCache.metadata.set(METADATA_LAST_FINALIZED_PROCESSED_KEY, 109);\n\n await expect(unfinalizedBlocks.init(jest.fn())).resolves.not.toThrow();\n });\n\n it('rewinds using blockForkReindex value if poi is not enabled', async () => {\n // Do this to \"disable\" poi\n (storeCache as any).poi = null;\n\n await storeCache.metadata.set(\n METADATA_UNFINALIZED_BLOCKS_KEY,\n JSON.stringify(<Header[]>[\n makeHeader(110),\n makeHeader(111),\n makeHeader(112),\n ]),\n );\n\n await storeCache.metadata.set(METADATA_LAST_FINALIZED_PROCESSED_KEY, 109);\n\n const rewind = jest.fn();\n\n await unfinalizedBlocks.init(rewind);\n\n // It should fall back to poi in this case\n expect(rewind).toHaveBeenCalledWith({ blockHeight: 0 });\n });\n});\n"]}
|