mirror of
https://github.com/cerc-io/watcher-ts
synced 2026-09-07 16:34:06 +00:00
Refactor state creation code (#204)
* Remove support for pushing state to IPFS * Move job handlers for state creation to util * Rename state creation related methods and objects * Update mock indexer used in graph-node testing * Fetch and merge diffs in batches while creating a state checkpoint * Fix timing logs while for state checkpoint creation * Refactor method to get state query result to util * Accept contracts for state verification in compare CLI config * Make method to update state status map synchronous
This commit is contained in:
@@ -82,18 +82,10 @@
|
||||
yarn
|
||||
```
|
||||
|
||||
* Run the IPFS (go-ipfs version 0.12.2) daemon:
|
||||
|
||||
```bash
|
||||
ipfs daemon
|
||||
```
|
||||
|
||||
* In the config file (`environments/local.toml`):
|
||||
|
||||
* Update the state checkpoint settings.
|
||||
|
||||
* Update the IPFS API address in `environments/local.toml`.
|
||||
|
||||
* Create the databases configured in `environments/local.toml`.
|
||||
|
||||
### Customize
|
||||
@@ -106,11 +98,11 @@
|
||||
|
||||
* Generating state:
|
||||
|
||||
* Edit the custom hook function `createInitialState` (triggered if the watcher passes the start block, checkpoint: `true`) in `src/hooks.ts` to save an initial state `IPLDBlock` using the `Indexer` object.
|
||||
* Edit the custom hook function `createInitialState` (triggered if the watcher passes the start block, checkpoint: `true`) in `src/hooks.ts` to save an initial `State` using the `Indexer` object.
|
||||
|
||||
* Edit the custom hook function `createStateDiff` (triggered on a block) in `src/hooks.ts` to save the state in a `diff` `IPLDBlock` using the `Indexer` object. The default state (if exists) is updated.
|
||||
* Edit the custom hook function `createStateDiff` (triggered on a block) in `src/hooks.ts` to save the state in a `diff` `State` using the `Indexer` object. The default state (if exists) is updated.
|
||||
|
||||
* Edit the custom hook function `createStateCheckpoint` (triggered just before default and CLI checkpoint) in `src/hooks.ts` to save the state in a `checkpoint` `IPLDBlock` using the `Indexer` object.
|
||||
* Edit the custom hook function `createStateCheckpoint` (triggered just before default and CLI checkpoint) in `src/hooks.ts` to save the state in a `checkpoint` `State` using the `Indexer` object.
|
||||
|
||||
### Run
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
className: IPLDBlock
|
||||
className: State
|
||||
indexOn:
|
||||
- columns:
|
||||
- cid
|
||||
+2
-6
@@ -1,10 +1,10 @@
|
||||
className: IpldStatus
|
||||
className: StateSyncStatus
|
||||
indexOn: []
|
||||
columns:
|
||||
- name: id
|
||||
tsType: number
|
||||
columnType: PrimaryGeneratedColumn
|
||||
- name: latestHooksBlockNumber
|
||||
- name: latestIndexedBlockNumber
|
||||
pgType: integer
|
||||
tsType: number
|
||||
columnType: Column
|
||||
@@ -12,10 +12,6 @@ columns:
|
||||
pgType: integer
|
||||
tsType: number
|
||||
columnType: Column
|
||||
- name: latestIPFSBlockNumber
|
||||
pgType: integer
|
||||
tsType: number
|
||||
columnType: Column
|
||||
imports:
|
||||
- toImport:
|
||||
- Entity
|
||||
@@ -178,8 +178,8 @@ export class Entity {
|
||||
this._addSyncStatusEntity();
|
||||
this._addContractEntity();
|
||||
this._addBlockProgressEntity();
|
||||
this._addIPLDBlockEntity();
|
||||
this._addIpldStatusEntity();
|
||||
this._addStateEntity();
|
||||
this._addStateSyncStatusEntity();
|
||||
|
||||
const template = Handlebars.compile(this._templateString);
|
||||
this._entities.forEach(entityObj => {
|
||||
@@ -278,13 +278,13 @@ export class Entity {
|
||||
this._entities.push(entity);
|
||||
}
|
||||
|
||||
_addIPLDBlockEntity (): void {
|
||||
const entity = yaml.load(fs.readFileSync(path.resolve(__dirname, TABLES_DIR, 'IPLDBlock.yaml'), 'utf8'));
|
||||
_addStateEntity (): void {
|
||||
const entity = yaml.load(fs.readFileSync(path.resolve(__dirname, TABLES_DIR, 'State.yaml'), 'utf8'));
|
||||
this._entities.push(entity);
|
||||
}
|
||||
|
||||
_addIpldStatusEntity (): void {
|
||||
const entity = yaml.load(fs.readFileSync(path.resolve(__dirname, TABLES_DIR, 'IpldStatus.yaml'), 'utf8'));
|
||||
_addStateSyncStatusEntity (): void {
|
||||
const entity = yaml.load(fs.readFileSync(path.resolve(__dirname, TABLES_DIR, 'StateSyncStatus.yaml'), 'utf8'));
|
||||
this._entities.push(entity);
|
||||
}
|
||||
|
||||
|
||||
@@ -297,25 +297,12 @@ function generateWatcher (visitor: Visitor, contracts: any[], config: any) {
|
||||
: process.stdout;
|
||||
visitor.exportClient(outStream, schemaContent, path.join(outputDir, 'src/gql'));
|
||||
|
||||
let resetOutStream, resetJQOutStream, resetStateOutStream, resetIPLDStateOutStream;
|
||||
const resetOutStream = fs.createWriteStream(path.join(outputDir, 'src/cli/reset.ts'));
|
||||
const resetJQOutStream = fs.createWriteStream(path.join(outputDir, 'src/cli/reset-cmds/job-queue.ts'));
|
||||
const resetWatcherOutStream = fs.createWriteStream(path.join(outputDir, 'src/cli/reset-cmds/watcher.ts'));
|
||||
const resetStateOutStream = fs.createWriteStream(path.join(outputDir, 'src/cli/reset-cmds/state.ts'));
|
||||
|
||||
if (outputDir) {
|
||||
resetOutStream = fs.createWriteStream(path.join(outputDir, 'src/cli/reset.ts'));
|
||||
resetJQOutStream = fs.createWriteStream(path.join(outputDir, 'src/cli/reset-cmds/job-queue.ts'));
|
||||
resetStateOutStream = fs.createWriteStream(path.join(outputDir, 'src/cli/reset-cmds/state.ts'));
|
||||
if (config.subgraphPath) {
|
||||
resetIPLDStateOutStream = fs.createWriteStream(path.join(outputDir, 'src/cli/reset-cmds/ipld-state.ts'));
|
||||
}
|
||||
} else {
|
||||
resetOutStream = process.stdout;
|
||||
resetJQOutStream = process.stdout;
|
||||
resetStateOutStream = process.stdout;
|
||||
if (config.subgraphPath) {
|
||||
resetIPLDStateOutStream = process.stdout;
|
||||
}
|
||||
}
|
||||
|
||||
visitor.exportReset(resetOutStream, resetJQOutStream, resetStateOutStream, resetIPLDStateOutStream, config.subgraphPath);
|
||||
visitor.exportReset(resetOutStream, resetJQOutStream, resetWatcherOutStream, resetStateOutStream, config.subgraphPath);
|
||||
|
||||
outStream = outputDir
|
||||
? fs.createWriteStream(path.join(outputDir, 'src/cli/export-state.ts'))
|
||||
|
||||
@@ -9,22 +9,22 @@ import { Writable } from 'stream';
|
||||
|
||||
const RESET_TEMPLATE_FILE = './templates/reset-template.handlebars';
|
||||
const RESET_JQ_TEMPLATE_FILE = './templates/reset-job-queue-template.handlebars';
|
||||
const RESET_WATCHER_TEMPLATE_FILE = './templates/reset-watcher-template.handlebars';
|
||||
const RESET_STATE_TEMPLATE_FILE = './templates/reset-state-template.handlebars';
|
||||
const RESET_IPLD_STATE_TEMPLATE_FILE = './templates/reset-ipld-state-template.handlebars';
|
||||
|
||||
export class Reset {
|
||||
_queries: Array<any>;
|
||||
_resetTemplateString: string;
|
||||
_resetJQTemplateString: string;
|
||||
_resetWatcherTemplateString: string;
|
||||
_resetStateTemplateString: string;
|
||||
_resetIPLDStateTemplateString: string;
|
||||
|
||||
constructor () {
|
||||
this._queries = [];
|
||||
this._resetTemplateString = fs.readFileSync(path.resolve(__dirname, RESET_TEMPLATE_FILE)).toString();
|
||||
this._resetJQTemplateString = fs.readFileSync(path.resolve(__dirname, RESET_JQ_TEMPLATE_FILE)).toString();
|
||||
this._resetWatcherTemplateString = fs.readFileSync(path.resolve(__dirname, RESET_WATCHER_TEMPLATE_FILE)).toString();
|
||||
this._resetStateTemplateString = fs.readFileSync(path.resolve(__dirname, RESET_STATE_TEMPLATE_FILE)).toString();
|
||||
this._resetIPLDStateTemplateString = fs.readFileSync(path.resolve(__dirname, RESET_IPLD_STATE_TEMPLATE_FILE)).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,13 +71,13 @@ export class Reset {
|
||||
*/
|
||||
|
||||
/**
|
||||
* Writes the reset.ts, job-queue.ts, state.ts files generated from templates to respective streams.
|
||||
* Writes the reset.ts, job-queue.ts, watcher.ts, state.ts files generated from templates to respective streams.
|
||||
* @param resetOutStream A writable output stream to write the reset file to.
|
||||
* @param resetJQOutStream A writable output stream to write the reset job-queue file to.
|
||||
* @param resetWatcherOutStream A writable output stream to write the reset watcher file to.
|
||||
* @param resetStateOutStream A writable output stream to write the reset state file to.
|
||||
* @param resetIPLDStateOutStream A writable output stream to write the reset IPLD state file to.
|
||||
*/
|
||||
exportReset (resetOutStream: Writable, resetJQOutStream: Writable, resetStateOutStream: Writable, resetIPLDStateOutStream: Writable | undefined, subgraphPath: string): void {
|
||||
exportReset (resetOutStream: Writable, resetJQOutStream: Writable, resetWatcherOutStream: Writable, resetStateOutStream: Writable, subgraphPath: string): void {
|
||||
const resetTemplate = Handlebars.compile(this._resetTemplateString);
|
||||
const resetString = resetTemplate({});
|
||||
resetOutStream.write(resetString);
|
||||
@@ -86,18 +86,16 @@ export class Reset {
|
||||
const resetJQString = resetJQTemplate({});
|
||||
resetJQOutStream.write(resetJQString);
|
||||
|
||||
const resetStateTemplate = Handlebars.compile(this._resetStateTemplateString);
|
||||
const resetWatcherTemplate = Handlebars.compile(this._resetWatcherTemplateString);
|
||||
const obj = {
|
||||
queries: this._queries,
|
||||
subgraphPath
|
||||
};
|
||||
const resetState = resetStateTemplate(obj);
|
||||
resetStateOutStream.write(resetState);
|
||||
const resetWatcher = resetWatcherTemplate(obj);
|
||||
resetWatcherOutStream.write(resetWatcher);
|
||||
|
||||
if (resetIPLDStateOutStream) {
|
||||
const resetIPLDStateTemplate = Handlebars.compile(this._resetIPLDStateTemplateString);
|
||||
const resetIPLDStateString = resetIPLDStateTemplate({});
|
||||
resetIPLDStateOutStream.write(resetIPLDStateString);
|
||||
}
|
||||
const resetStateTemplate = Handlebars.compile(this._resetStateTemplateString);
|
||||
const resetState = resetStateTemplate({});
|
||||
resetStateOutStream.write(resetState);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,9 +98,9 @@ export class Schema {
|
||||
// Add type and query for SyncStatus.
|
||||
this._addSyncStatus();
|
||||
|
||||
// Add IPLDBlock type and queries.
|
||||
this._addIPLDType();
|
||||
this._addIPLDQuery();
|
||||
// Add State type and queries.
|
||||
this._addStateType();
|
||||
this._addStateQuery();
|
||||
|
||||
// Build the schema.
|
||||
return this._composer.buildSchema();
|
||||
@@ -354,9 +354,9 @@ export class Schema {
|
||||
});
|
||||
}
|
||||
|
||||
_addIPLDType (): void {
|
||||
_addStateType (): void {
|
||||
const typeComposer = this._composer.createObjectTC({
|
||||
name: 'ResultIPLDBlock',
|
||||
name: 'ResultState',
|
||||
fields: {
|
||||
block: () => this._composer.getOTC('_Block_').NonNull,
|
||||
contractAddress: 'String!',
|
||||
@@ -368,10 +368,10 @@ export class Schema {
|
||||
this._composer.addSchemaMustHaveType(typeComposer);
|
||||
}
|
||||
|
||||
_addIPLDQuery (): void {
|
||||
_addStateQuery (): void {
|
||||
this._composer.Query.addFields({
|
||||
getStateByCID: {
|
||||
type: this._composer.getOTC('ResultIPLDBlock'),
|
||||
type: this._composer.getOTC('ResultState'),
|
||||
args: {
|
||||
cid: 'String!'
|
||||
}
|
||||
@@ -380,7 +380,7 @@ export class Schema {
|
||||
|
||||
this._composer.Query.addFields({
|
||||
getState: {
|
||||
type: this._composer.getOTC('ResultIPLDBlock'),
|
||||
type: this._composer.getOTC('ResultState'),
|
||||
args: {
|
||||
blockHash: 'String!',
|
||||
contractAddress: 'String!',
|
||||
|
||||
@@ -54,12 +54,12 @@ export const handler = async (argv: any): Promise<void> => {
|
||||
graphWatcher.setIndexer(indexer);
|
||||
await graphWatcher.init();
|
||||
|
||||
const ipldBlock = await indexer.getIPLDBlockByCid(argv.cid);
|
||||
assert(ipldBlock, 'IPLDBlock for the provided CID doesn\'t exist.');
|
||||
const data = indexer.getIPLDData(ipldBlock);
|
||||
const state = await indexer.getStateByCID(argv.cid);
|
||||
assert(state, 'State for the provided CID doesn\'t exist.');
|
||||
const data = indexer.getStateData(state);
|
||||
|
||||
log(`Verifying checkpoint data for contract ${ipldBlock.contractAddress}`);
|
||||
await verifyCheckpointData(graphDb, ipldBlock.block, data);
|
||||
log(`Verifying checkpoint data for contract ${state.contractAddress}`);
|
||||
await verifyCheckpointData(graphDb, state.block, data);
|
||||
log('Checkpoint data verified');
|
||||
|
||||
await db.close();
|
||||
|
||||
@@ -9,9 +9,6 @@
|
||||
# Checkpoint interval in number of blocks.
|
||||
checkpointInterval = 2000
|
||||
|
||||
# IPFS API address (can be taken from the output on running the IPFS daemon).
|
||||
ipfsApiAddr = "/ip4/127.0.0.1/tcp/5001"
|
||||
|
||||
{{#if (subgraphPath)}}
|
||||
subgraphPath = "{{subgraphPath}}"
|
||||
|
||||
|
||||
@@ -11,9 +11,9 @@ import { Database as BaseDatabase, DatabaseInterface, QueryOptions, StateKind, W
|
||||
import { Contract } from './entity/Contract';
|
||||
import { Event } from './entity/Event';
|
||||
import { SyncStatus } from './entity/SyncStatus';
|
||||
import { IpldStatus } from './entity/IpldStatus';
|
||||
import { StateSyncStatus } from './entity/StateSyncStatus';
|
||||
import { BlockProgress } from './entity/BlockProgress';
|
||||
import { IPLDBlock } from './entity/IPLDBlock';
|
||||
import { State } from './entity/State';
|
||||
{{#each queries as | query |}}
|
||||
import { {{query.entityName}} } from './entity/{{query.entityName}}';
|
||||
{{/each}}
|
||||
@@ -79,75 +79,69 @@ export class Database implements DatabaseInterface {
|
||||
}
|
||||
|
||||
{{/each}}
|
||||
getNewIPLDBlock (): IPLDBlock {
|
||||
return new IPLDBlock();
|
||||
getNewState (): State {
|
||||
return new State();
|
||||
}
|
||||
|
||||
async getIPLDBlocks (where: FindConditions<IPLDBlock>): Promise<IPLDBlock[]> {
|
||||
const repo = this._conn.getRepository(IPLDBlock);
|
||||
async getStates (where: FindConditions<State>): Promise<State[]> {
|
||||
const repo = this._conn.getRepository(State);
|
||||
|
||||
return this._baseDatabase.getIPLDBlocks(repo, where);
|
||||
return this._baseDatabase.getStates(repo, where);
|
||||
}
|
||||
|
||||
async getLatestIPLDBlock (contractAddress: string, kind: StateKind | null, blockNumber?: number): Promise<IPLDBlock | undefined> {
|
||||
const repo = this._conn.getRepository(IPLDBlock);
|
||||
async getLatestState (contractAddress: string, kind: StateKind | null, blockNumber?: number): Promise<State | undefined> {
|
||||
const repo = this._conn.getRepository(State);
|
||||
|
||||
return this._baseDatabase.getLatestIPLDBlock(repo, contractAddress, kind, blockNumber);
|
||||
return this._baseDatabase.getLatestState(repo, contractAddress, kind, blockNumber);
|
||||
}
|
||||
|
||||
async getPrevIPLDBlock (blockHash: string, contractAddress: string, kind?: string): Promise<IPLDBlock | undefined> {
|
||||
const repo = this._conn.getRepository(IPLDBlock);
|
||||
async getPrevState (blockHash: string, contractAddress: string, kind?: string): Promise<State | undefined> {
|
||||
const repo = this._conn.getRepository(State);
|
||||
|
||||
return this._baseDatabase.getPrevIPLDBlock(repo, blockHash, contractAddress, kind);
|
||||
return this._baseDatabase.getPrevState(repo, blockHash, contractAddress, kind);
|
||||
}
|
||||
|
||||
// Fetch all diff IPLDBlocks after the specified block number.
|
||||
async getDiffIPLDBlocksInRange (contractAddress: string, startBlock: number, endBlock: number): Promise<IPLDBlock[]> {
|
||||
const repo = this._conn.getRepository(IPLDBlock);
|
||||
// Fetch all diff States after the specified block number.
|
||||
async getDiffStatesInRange (contractAddress: string, startblock: number, endBlock: number): Promise<State[]> {
|
||||
const repo = this._conn.getRepository(State);
|
||||
|
||||
return this._baseDatabase.getDiffIPLDBlocksInRange(repo, contractAddress, startBlock, endBlock);
|
||||
return this._baseDatabase.getDiffStatesInRange(repo, contractAddress, startblock, endBlock);
|
||||
}
|
||||
|
||||
async saveOrUpdateIPLDBlock (dbTx: QueryRunner, ipldBlock: IPLDBlock): Promise<IPLDBlock> {
|
||||
const repo = dbTx.manager.getRepository(IPLDBlock);
|
||||
async saveOrUpdateState (dbTx: QueryRunner, state: State): Promise<State> {
|
||||
const repo = dbTx.manager.getRepository(State);
|
||||
|
||||
return this._baseDatabase.saveOrUpdateIPLDBlock(repo, ipldBlock);
|
||||
return this._baseDatabase.saveOrUpdateState(repo, state);
|
||||
}
|
||||
|
||||
async removeIPLDBlocks (dbTx: QueryRunner, blockNumber: number, kind: string): Promise<void> {
|
||||
const repo = dbTx.manager.getRepository(IPLDBlock);
|
||||
async removeStates (dbTx: QueryRunner, blockNumber: number, kind: string): Promise<void> {
|
||||
const repo = dbTx.manager.getRepository(State);
|
||||
|
||||
await this._baseDatabase.removeIPLDBlocks(repo, blockNumber, kind);
|
||||
await this._baseDatabase.removeStates(repo, blockNumber, kind);
|
||||
}
|
||||
|
||||
async removeIPLDBlocksAfterBlock (dbTx: QueryRunner, blockNumber: number): Promise<void> {
|
||||
const repo = dbTx.manager.getRepository(IPLDBlock);
|
||||
async removeStatesAfterBlock (dbTx: QueryRunner, blockNumber: number): Promise<void> {
|
||||
const repo = dbTx.manager.getRepository(State);
|
||||
|
||||
await this._baseDatabase.removeIPLDBlocksAfterBlock(repo, blockNumber);
|
||||
await this._baseDatabase.removeStatesAfterBlock(repo, blockNumber);
|
||||
}
|
||||
|
||||
async getIPLDStatus (): Promise<IpldStatus | undefined> {
|
||||
const repo = this._conn.getRepository(IpldStatus);
|
||||
async getStateSyncStatus (): Promise<StateSyncStatus | undefined> {
|
||||
const repo = this._conn.getRepository(StateSyncStatus);
|
||||
|
||||
return this._baseDatabase.getIPLDStatus(repo);
|
||||
return this._baseDatabase.getStateSyncStatus(repo);
|
||||
}
|
||||
|
||||
async updateIPLDStatusHooksBlock (queryRunner: QueryRunner, blockNumber: number, force?: boolean): Promise<IpldStatus> {
|
||||
const repo = queryRunner.manager.getRepository(IpldStatus);
|
||||
async updateStateSyncStatusIndexedBlock (queryRunner: QueryRunner, blockNumber: number, force?: boolean): Promise<StateSyncStatus> {
|
||||
const repo = queryRunner.manager.getRepository(StateSyncStatus);
|
||||
|
||||
return this._baseDatabase.updateIPLDStatusHooksBlock(repo, blockNumber, force);
|
||||
return this._baseDatabase.updateStateSyncStatusIndexedBlock(repo, blockNumber, force);
|
||||
}
|
||||
|
||||
async updateIPLDStatusCheckpointBlock (queryRunner: QueryRunner, blockNumber: number, force?: boolean): Promise<IpldStatus> {
|
||||
const repo = queryRunner.manager.getRepository(IpldStatus);
|
||||
async updateStateSyncStatusCheckpointBlock (queryRunner: QueryRunner, blockNumber: number, force?: boolean): Promise<StateSyncStatus> {
|
||||
const repo = queryRunner.manager.getRepository(StateSyncStatus);
|
||||
|
||||
return this._baseDatabase.updateIPLDStatusCheckpointBlock(repo, blockNumber, force);
|
||||
}
|
||||
|
||||
async updateIPLDStatusIPFSBlock (queryRunner: QueryRunner, blockNumber: number, force?: boolean): Promise<IpldStatus> {
|
||||
const repo = queryRunner.manager.getRepository(IpldStatus);
|
||||
|
||||
return this._baseDatabase.updateIPLDStatusIPFSBlock(repo, blockNumber, force);
|
||||
return this._baseDatabase.updateStateSyncStatusCheckpointBlock(repo, blockNumber, force);
|
||||
}
|
||||
|
||||
async getContracts (): Promise<Contract[]> {
|
||||
|
||||
@@ -85,18 +85,18 @@ const main = async (): Promise<void> => {
|
||||
const exportData: any = {
|
||||
snapshotBlock: {},
|
||||
contracts: [],
|
||||
ipldCheckpoints: []
|
||||
stateCheckpoints: []
|
||||
};
|
||||
|
||||
const contracts = await db.getContracts();
|
||||
|
||||
// Get latest block with hooks processed.
|
||||
let block = await indexer.getLatestHooksProcessedBlock();
|
||||
let block = await indexer.getLatestStateIndexedBlock();
|
||||
assert(block);
|
||||
|
||||
if (argv.blockNumber) {
|
||||
if (argv.blockNumber > block.blockNumber) {
|
||||
throw new Error(`Export snapshot block height ${argv.blockNumber} should be less than latest hooks processed block height ${block.blockNumber}`);
|
||||
throw new Error(`Export snapshot block height ${argv.blockNumber} should be less than latest state indexed block height ${block.blockNumber}`);
|
||||
}
|
||||
|
||||
const blocksAtSnapshotHeight = await indexer.getBlocksAtHeight(argv.blockNumber, false);
|
||||
@@ -133,19 +133,15 @@ const main = async (): Promise<void> => {
|
||||
if (contract.checkpoint) {
|
||||
await indexer.createCheckpoint(contract.address, block.blockHash);
|
||||
|
||||
const ipldBlock = await indexer.getLatestIPLDBlock(contract.address, StateKind.Checkpoint, block.blockNumber);
|
||||
assert(ipldBlock);
|
||||
const state = await indexer.getLatestState(contract.address, StateKind.Checkpoint, block.blockNumber);
|
||||
assert(state);
|
||||
|
||||
const data = indexer.getIPLDData(ipldBlock);
|
||||
const data = indexer.getStateData(state);
|
||||
|
||||
if (indexer.isIPFSConfigured()) {
|
||||
await indexer.pushToIPFS(data);
|
||||
}
|
||||
|
||||
exportData.ipldCheckpoints.push({
|
||||
contractAddress: ipldBlock.contractAddress,
|
||||
cid: ipldBlock.cid,
|
||||
kind: ipldBlock.kind,
|
||||
exportData.stateCheckpoints.push({
|
||||
contractAddress: state.contractAddress,
|
||||
cid: state.cid,
|
||||
kind: state.kind,
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ export const fillState = async (
|
||||
log(`Filling state for subgraph entities in range: [${startBlock}, ${endBlock}]`);
|
||||
|
||||
// Check that there are no existing diffs in this range
|
||||
const existingStates = await indexer.getIPLDBlocks({ block: { blockNumber: Between(startBlock, endBlock) } });
|
||||
const existingStates = await indexer.getStates({ block: { blockNumber: Between(startBlock, endBlock) } });
|
||||
if (existingStates.length > 0) {
|
||||
log('found existing state(s) in the given range');
|
||||
process.exit(1);
|
||||
@@ -93,12 +93,12 @@ export const fillState = async (
|
||||
|
||||
// Persist subgraph state to the DB
|
||||
await indexer.dumpSubgraphState(blockHash, true);
|
||||
await indexer.updateStateSyncStatusIndexedBlock(blockNumber);
|
||||
|
||||
// Create checkpoints
|
||||
await indexer.processCheckpoint(blockHash);
|
||||
await indexer.updateStateSyncStatusCheckpointBlock(blockNumber);
|
||||
}
|
||||
|
||||
// TODO: Push state to IPFS
|
||||
|
||||
log(`Filled state for subgraph entities in range: [${startBlock}, ${endBlock}]`);
|
||||
};
|
||||
|
||||
@@ -20,19 +20,19 @@ export async function createInitialState (indexer: Indexer, contractAddress: str
|
||||
assert(blockHash);
|
||||
assert(contractAddress);
|
||||
|
||||
// Store the desired initial state in an IPLDBlock.
|
||||
const ipldBlockData: any = {
|
||||
// Store an empty State.
|
||||
const stateData: any = {
|
||||
state: {}
|
||||
};
|
||||
|
||||
// Use updateStateForElementaryType to update initial state with an elementary property.
|
||||
// Eg. const ipldBlockData = updateStateForElementaryType(ipldBlockData, '_totalBalance', result.value.toString());
|
||||
// Eg. const stateData = updateStateForElementaryType(stateData, '_totalBalance', result.value.toString());
|
||||
|
||||
// Use updateStateForMappingType to update initial state with a nested property.
|
||||
// Eg. const ipldBlockData = updateStateForMappingType(ipldBlockData, '_allowances', [owner, spender], allowance.value.toString());
|
||||
// Eg. const stateData = updateStateForMappingType(stateData, '_allowances', [owner, spender], allowance.value.toString());
|
||||
|
||||
// Return initial state data to be saved.
|
||||
return ipldBlockData;
|
||||
return stateData;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,7 +20,7 @@ import * as codec from '@ipld/dag-cbor';
|
||||
import { Database } from '../database';
|
||||
import { Indexer } from '../indexer';
|
||||
import { EventWatcher } from '../events';
|
||||
import { IPLDBlock } from '../entity/IPLDBlock';
|
||||
import { State } from '../entity/State';
|
||||
|
||||
const log = debug('vulcanize:import-state');
|
||||
|
||||
@@ -106,18 +106,18 @@ export const main = async (): Promise<any> => {
|
||||
const block = await indexer.getBlockProgress(importData.snapshotBlock.blockHash);
|
||||
assert(block);
|
||||
|
||||
// Fill the IPLDBlocks.
|
||||
for (const checkpoint of importData.ipldCheckpoints) {
|
||||
let ipldBlock = new IPLDBlock();
|
||||
// Fill the States.
|
||||
for (const checkpoint of importData.stateCheckpoints) {
|
||||
let state = new State();
|
||||
|
||||
ipldBlock = Object.assign(ipldBlock, checkpoint);
|
||||
ipldBlock.block = block;
|
||||
state = Object.assign(state, checkpoint);
|
||||
state.block = block;
|
||||
|
||||
ipldBlock.data = Buffer.from(codec.encode(ipldBlock.data));
|
||||
state.data = Buffer.from(codec.encode(state.data));
|
||||
|
||||
ipldBlock = await indexer.saveOrUpdateIPLDBlock(ipldBlock);
|
||||
state = await indexer.saveOrUpdateState(state);
|
||||
{{#if (subgraphPath)}}
|
||||
await graphWatcher.updateEntitiesFromIPLDState(ipldBlock);
|
||||
await graphWatcher.updateEntitiesFromState(state);
|
||||
{{/if}}
|
||||
}
|
||||
|
||||
@@ -126,12 +126,12 @@ export const main = async (): Promise<any> => {
|
||||
await indexer.updateBlockProgress(block, block.lastProcessedEventIndex);
|
||||
await indexer.updateSyncStatusChainHead(block.blockHash, block.blockNumber);
|
||||
await indexer.updateSyncStatusIndexedBlock(block.blockHash, block.blockNumber);
|
||||
await indexer.updateIPLDStatusHooksBlock(block.blockNumber);
|
||||
await indexer.updateIPLDStatusCheckpointBlock(block.blockNumber);
|
||||
await indexer.updateStateSyncStatusIndexedBlock(block.blockNumber);
|
||||
await indexer.updateStateSyncStatusCheckpointBlock(block.blockNumber);
|
||||
|
||||
// The 'diff_staged' and 'init' IPLD blocks are unnecessary as checkpoints have been already created for the snapshot block.
|
||||
await indexer.removeIPLDBlocks(block.blockNumber, StateKind.Init);
|
||||
await indexer.removeIPLDBlocks(block.blockNumber, StateKind.DiffStaged);
|
||||
// The 'diff_staged' and 'init' State entries are unnecessary as checkpoints have been already created for the snapshot block.
|
||||
await indexer.removeStates(block.blockNumber, StateKind.Init);
|
||||
await indexer.removeStates(block.blockNumber, StateKind.DiffStaged);
|
||||
|
||||
log(`Import completed for snapshot block at height ${block.blockNumber}`);
|
||||
};
|
||||
|
||||
@@ -8,11 +8,12 @@ import { DeepPartial, FindConditions, FindManyOptions } from 'typeorm';
|
||||
import JSONbig from 'json-bigint';
|
||||
import { ethers } from 'ethers';
|
||||
import _ from 'lodash';
|
||||
{{#if (subgraphPath)}}
|
||||
import { SelectionNode } from 'graphql';
|
||||
{{/if}}
|
||||
|
||||
import { JsonFragment } from '@ethersproject/abi';
|
||||
import { BaseProvider } from '@ethersproject/providers';
|
||||
import * as codec from '@ipld/dag-cbor';
|
||||
import { EthClient } from '@cerc-io/ipld-eth-client';
|
||||
import { MappingKey, StorageLayout } from '@cerc-io/solidity-mapper';
|
||||
import {
|
||||
@@ -29,10 +30,8 @@ import {
|
||||
{{#if (subgraphPath)}}
|
||||
BlockHeight,
|
||||
{{/if}}
|
||||
IPFSClient,
|
||||
StateKind,
|
||||
IpldStatus as IpldStatusInterface,
|
||||
ResultIPLDBlock
|
||||
StateStatus
|
||||
} from '@cerc-io/util';
|
||||
{{#if (subgraphPath)}}
|
||||
import { GraphWatcher } from '@cerc-io/graph-node';
|
||||
@@ -46,9 +45,9 @@ import { createInitialState, handleEvent, createStateDiff, createStateCheckpoint
|
||||
import { Contract } from './entity/Contract';
|
||||
import { Event } from './entity/Event';
|
||||
import { SyncStatus } from './entity/SyncStatus';
|
||||
import { IpldStatus } from './entity/IpldStatus';
|
||||
import { StateSyncStatus } from './entity/StateSyncStatus';
|
||||
import { BlockProgress } from './entity/BlockProgress';
|
||||
import { IPLDBlock } from './entity/IPLDBlock';
|
||||
import { State } from './entity/State';
|
||||
|
||||
{{#each subgraphEntities as | subgraphEntity |}}
|
||||
import { {{subgraphEntity.className}} } from './entity/{{subgraphEntity.className}}';
|
||||
@@ -103,8 +102,6 @@ export class Indexer implements IndexerInterface {
|
||||
_storageLayoutMap: Map<string, StorageLayout>
|
||||
_contractMap: Map<string, ethers.utils.Interface>
|
||||
|
||||
_ipfsClient: IPFSClient
|
||||
|
||||
{{#if (subgraphPath)}}
|
||||
_entityTypesMap: Map<string, { [key: string]: string }>
|
||||
_relationsMap: Map<any, { [key: string]: any }>
|
||||
@@ -120,8 +117,7 @@ export class Indexer implements IndexerInterface {
|
||||
this._ethClient = ethClient;
|
||||
this._ethProvider = ethProvider;
|
||||
this._serverConfig = serverConfig;
|
||||
this._ipfsClient = new IPFSClient(this._serverConfig.ipfsApiAddr);
|
||||
this._baseIndexer = new BaseIndexer(this._serverConfig, this._db, this._ethClient, this._ethProvider, jobQueue, this._ipfsClient);
|
||||
this._baseIndexer = new BaseIndexer(this._serverConfig, this._db, this._ethClient, this._ethProvider, jobQueue);
|
||||
{{#if (subgraphPath)}}
|
||||
this._graphWatcher = graphWatcher;
|
||||
{{/if}}
|
||||
@@ -170,7 +166,7 @@ export class Indexer implements IndexerInterface {
|
||||
|
||||
async init (): Promise<void> {
|
||||
await this._baseIndexer.fetchContracts();
|
||||
await this._baseIndexer.fetchIPLDStatus();
|
||||
await this._baseIndexer.fetchStateStatus();
|
||||
}
|
||||
|
||||
getResultEvent (event: Event): ResultEvent {
|
||||
@@ -208,26 +204,6 @@ export class Indexer implements IndexerInterface {
|
||||
};
|
||||
}
|
||||
|
||||
getResultIPLDBlock (ipldBlock: IPLDBlock): ResultIPLDBlock {
|
||||
const block = ipldBlock.block;
|
||||
|
||||
const data = codec.decode(Buffer.from(ipldBlock.data)) as any;
|
||||
|
||||
return {
|
||||
block: {
|
||||
cid: block.cid,
|
||||
hash: block.blockHash,
|
||||
number: block.blockNumber,
|
||||
timestamp: block.blockTimestamp,
|
||||
parentHash: block.parentHash
|
||||
},
|
||||
contractAddress: ipldBlock.contractAddress,
|
||||
cid: ipldBlock.cid,
|
||||
kind: ipldBlock.kind,
|
||||
data: JSON.stringify(data)
|
||||
};
|
||||
}
|
||||
|
||||
{{#each queries as | query |}}
|
||||
async {{query.name}} (blockHash: string, contractAddress: string
|
||||
{{~#each query.params}}, {{this.name~}}: {{this.type~}} {{/each}}
|
||||
@@ -324,10 +300,6 @@ export class Indexer implements IndexerInterface {
|
||||
);
|
||||
}
|
||||
|
||||
async pushToIPFS (data: any): Promise<void> {
|
||||
await this._baseIndexer.pushToIPFS(data);
|
||||
}
|
||||
|
||||
async processInitialState (contractAddress: string, blockHash: string): Promise<any> {
|
||||
// Call initial state hook.
|
||||
return createInitialState(this, contractAddress, blockHash);
|
||||
@@ -362,32 +334,28 @@ export class Indexer implements IndexerInterface {
|
||||
return this._baseIndexer.processCLICheckpoint(this, contractAddress, blockHash);
|
||||
}
|
||||
|
||||
async getPrevIPLDBlock (blockHash: string, contractAddress: string, kind?: string): Promise<IPLDBlock | undefined> {
|
||||
return this._db.getPrevIPLDBlock(blockHash, contractAddress, kind);
|
||||
async getPrevState (blockHash: string, contractAddress: string, kind?: string): Promise<State | undefined> {
|
||||
return this._db.getPrevState(blockHash, contractAddress, kind);
|
||||
}
|
||||
|
||||
async getLatestIPLDBlock (contractAddress: string, kind: StateKind | null, blockNumber?: number): Promise<IPLDBlock | undefined> {
|
||||
return this._db.getLatestIPLDBlock(contractAddress, kind, blockNumber);
|
||||
async getLatestState (contractAddress: string, kind: StateKind | null, blockNumber?: number): Promise<State | undefined> {
|
||||
return this._db.getLatestState(contractAddress, kind, blockNumber);
|
||||
}
|
||||
|
||||
async getIPLDBlocksByHash (blockHash: string): Promise<IPLDBlock[]> {
|
||||
return this._baseIndexer.getIPLDBlocksByHash(blockHash);
|
||||
async getStatesByHash (blockHash: string): Promise<State[]> {
|
||||
return this._baseIndexer.getStatesByHash(blockHash);
|
||||
}
|
||||
|
||||
async getIPLDBlockByCid (cid: string): Promise<IPLDBlock | undefined> {
|
||||
return this._baseIndexer.getIPLDBlockByCid(cid);
|
||||
async getStateByCID (cid: string): Promise<State | undefined> {
|
||||
return this._baseIndexer.getStateByCID(cid);
|
||||
}
|
||||
|
||||
async getIPLDBlocks (where: FindConditions<IPLDBlock>): Promise<IPLDBlock[]> {
|
||||
return this._db.getIPLDBlocks(where);
|
||||
async getStates (where: FindConditions<State>): Promise<State[]> {
|
||||
return this._db.getStates(where);
|
||||
}
|
||||
|
||||
getIPLDData (ipldBlock: IPLDBlock): any {
|
||||
return this._baseIndexer.getIPLDData(ipldBlock);
|
||||
}
|
||||
|
||||
isIPFSConfigured (): boolean {
|
||||
return this._baseIndexer.isIPFSConfigured();
|
||||
getStateData (state: State): any {
|
||||
return this._baseIndexer.getStateData(state);
|
||||
}
|
||||
|
||||
// Method used to create auto diffs (diff_staged).
|
||||
@@ -425,12 +393,12 @@ export class Indexer implements IndexerInterface {
|
||||
await this._baseIndexer.createInit(this, blockHash, blockNumber);
|
||||
}
|
||||
|
||||
async saveOrUpdateIPLDBlock (ipldBlock: IPLDBlock): Promise<IPLDBlock> {
|
||||
return this._baseIndexer.saveOrUpdateIPLDBlock(ipldBlock);
|
||||
async saveOrUpdateState (state: State): Promise<State> {
|
||||
return this._baseIndexer.saveOrUpdateState(state);
|
||||
}
|
||||
|
||||
async removeIPLDBlocks (blockNumber: number, kind: StateKind): Promise<void> {
|
||||
await this._baseIndexer.removeIPLDBlocks(blockNumber, kind);
|
||||
async removeStates (blockNumber: number, kind: StateKind): Promise<void> {
|
||||
await this._baseIndexer.removeStates(blockNumber, kind);
|
||||
}
|
||||
|
||||
{{#if (subgraphPath)}}
|
||||
@@ -466,8 +434,8 @@ export class Indexer implements IndexerInterface {
|
||||
async processBlock (blockProgress: BlockProgress): Promise<void> {
|
||||
// Call a function to create initial state for contracts.
|
||||
await this._baseIndexer.createInit(this, blockProgress.blockHash, blockProgress.blockNumber);
|
||||
|
||||
{{#if (subgraphPath)}}
|
||||
|
||||
this._graphWatcher.updateEntityCacheFrothyBlocks(blockProgress);
|
||||
{{/if}}
|
||||
}
|
||||
@@ -499,16 +467,16 @@ export class Indexer implements IndexerInterface {
|
||||
};
|
||||
}
|
||||
|
||||
async getIPLDStatus (): Promise<IpldStatus | undefined> {
|
||||
return this._db.getIPLDStatus();
|
||||
async getStateSyncStatus (): Promise<StateSyncStatus | undefined> {
|
||||
return this._db.getStateSyncStatus();
|
||||
}
|
||||
|
||||
async updateIPLDStatusHooksBlock (blockNumber: number, force?: boolean): Promise<IpldStatus> {
|
||||
async updateStateSyncStatusIndexedBlock (blockNumber: number, force?: boolean): Promise<StateSyncStatus> {
|
||||
const dbTx = await this._db.createTransactionRunner();
|
||||
let res;
|
||||
|
||||
try {
|
||||
res = await this._db.updateIPLDStatusHooksBlock(dbTx, blockNumber, force);
|
||||
res = await this._db.updateStateSyncStatusIndexedBlock(dbTx, blockNumber, force);
|
||||
await dbTx.commitTransaction();
|
||||
} catch (error) {
|
||||
await dbTx.rollbackTransaction();
|
||||
@@ -520,29 +488,12 @@ export class Indexer implements IndexerInterface {
|
||||
return res;
|
||||
}
|
||||
|
||||
async updateIPLDStatusCheckpointBlock (blockNumber: number, force?: boolean): Promise<IpldStatus> {
|
||||
async updateStateSyncStatusCheckpointBlock (blockNumber: number, force?: boolean): Promise<StateSyncStatus> {
|
||||
const dbTx = await this._db.createTransactionRunner();
|
||||
let res;
|
||||
|
||||
try {
|
||||
res = await this._db.updateIPLDStatusCheckpointBlock(dbTx, blockNumber, force);
|
||||
await dbTx.commitTransaction();
|
||||
} catch (error) {
|
||||
await dbTx.rollbackTransaction();
|
||||
throw error;
|
||||
} finally {
|
||||
await dbTx.release();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
async updateIPLDStatusIPFSBlock (blockNumber: number, force?: boolean): Promise<IpldStatus> {
|
||||
const dbTx = await this._db.createTransactionRunner();
|
||||
let res;
|
||||
|
||||
try {
|
||||
res = await this._db.updateIPLDStatusIPFSBlock(dbTx, blockNumber, force);
|
||||
res = await this._db.updateStateSyncStatusCheckpointBlock(dbTx, blockNumber, force);
|
||||
await dbTx.commitTransaction();
|
||||
} catch (error) {
|
||||
await dbTx.rollbackTransaction();
|
||||
@@ -564,16 +515,16 @@ export class Indexer implements IndexerInterface {
|
||||
return latestCanonicalBlock;
|
||||
}
|
||||
|
||||
async getLatestHooksProcessedBlock (): Promise<BlockProgress> {
|
||||
return this._baseIndexer.getLatestHooksProcessedBlock();
|
||||
async getLatestStateIndexedBlock (): Promise<BlockProgress> {
|
||||
return this._baseIndexer.getLatestStateIndexedBlock();
|
||||
}
|
||||
|
||||
async watchContract (address: string, kind: string, checkpoint: boolean, startingBlock: number): Promise<void> {
|
||||
return this._baseIndexer.watchContract(address, kind, checkpoint, startingBlock);
|
||||
}
|
||||
|
||||
async updateIPLDStatusMap (address: string, ipldStatus: IpldStatusInterface): Promise<void> {
|
||||
await this._baseIndexer.updateIPLDStatusMap(address, ipldStatus);
|
||||
updateStateStatusMap (address: string, stateStatus: StateStatus): void {
|
||||
this._baseIndexer.updateStateStatusMap(address, stateStatus);
|
||||
}
|
||||
|
||||
cacheContract (contract: Contract): void {
|
||||
|
||||
@@ -71,12 +71,12 @@ const main = async (): Promise<void> => {
|
||||
await graphWatcher.init();
|
||||
{{/if}}
|
||||
|
||||
const ipldBlock = await indexer.getIPLDBlockByCid(argv.cid);
|
||||
assert(ipldBlock, 'IPLDBlock for the provided CID doesn\'t exist.');
|
||||
const state = await indexer.getStateByCID(argv.cid);
|
||||
assert(state, 'State for the provided CID doesn\'t exist.');
|
||||
|
||||
const ipldData = await indexer.getIPLDData(ipldBlock);
|
||||
const stateData = await indexer.getStateData(state);
|
||||
|
||||
log(util.inspect(ipldData, false, null));
|
||||
log(util.inspect(stateData, false, null));
|
||||
};
|
||||
|
||||
main().catch(err => {
|
||||
|
||||
@@ -20,7 +20,6 @@ import {
|
||||
QUEUE_EVENT_PROCESSING,
|
||||
QUEUE_BLOCK_CHECKPOINT,
|
||||
QUEUE_HOOKS,
|
||||
QUEUE_IPFS,
|
||||
JOB_KIND_PRUNE,
|
||||
JobQueueConfig,
|
||||
DEFAULT_CONFIG_PATH,
|
||||
@@ -54,21 +53,11 @@ export class JobRunner {
|
||||
await this.subscribeEventProcessingQueue();
|
||||
await this.subscribeBlockCheckpointQueue();
|
||||
await this.subscribeHooksQueue();
|
||||
await this.subscribeIPFSQueue();
|
||||
}
|
||||
|
||||
async subscribeBlockProcessingQueue (): Promise<void> {
|
||||
await this._jobQueue.subscribe(QUEUE_BLOCK_PROCESSING, async (job) => {
|
||||
await this._baseJobRunner.processBlock(job);
|
||||
|
||||
const { data: { kind } } = job;
|
||||
|
||||
// If it's a pruning job: Create a hooks job.
|
||||
if (kind === JOB_KIND_PRUNE) {
|
||||
await this.createHooksJob();
|
||||
}
|
||||
|
||||
await this._jobQueue.markComplete(job);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -80,165 +69,15 @@ export class JobRunner {
|
||||
|
||||
async subscribeHooksQueue (): Promise<void> {
|
||||
await this._jobQueue.subscribe(QUEUE_HOOKS, async (job) => {
|
||||
const { data: { blockHash, blockNumber } } = job;
|
||||
|
||||
// Get the current IPLD Status.
|
||||
const ipldStatus = await this._indexer.getIPLDStatus();
|
||||
|
||||
if (ipldStatus) {
|
||||
if (ipldStatus.latestHooksBlockNumber < (blockNumber - 1)) {
|
||||
// Create hooks job for parent block.
|
||||
const [parentBlock] = await this._indexer.getBlocksAtHeight(blockNumber - 1, false);
|
||||
await this.createHooksJob(parentBlock.blockHash, parentBlock.blockNumber);
|
||||
|
||||
const message = `Hooks for blockNumber ${blockNumber - 1} not processed yet, aborting`;
|
||||
log(message);
|
||||
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
if (ipldStatus.latestHooksBlockNumber > (blockNumber - 1)) {
|
||||
log(`Hooks for blockNumber ${blockNumber} already processed`);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Process the hooks for the given block number.
|
||||
await this._indexer.processCanonicalBlock(blockHash, blockNumber);
|
||||
|
||||
// Update the IPLD status.
|
||||
await this._indexer.updateIPLDStatusHooksBlock(blockNumber);
|
||||
|
||||
// Create a checkpoint job after completion of a hook job.
|
||||
await this.createCheckpointJob(blockHash, blockNumber);
|
||||
|
||||
await this._jobQueue.markComplete(job);
|
||||
await this._baseJobRunner.processHooks(job);
|
||||
});
|
||||
}
|
||||
|
||||
async subscribeBlockCheckpointQueue (): Promise<void> {
|
||||
await this._jobQueue.subscribe(QUEUE_BLOCK_CHECKPOINT, async (job) => {
|
||||
const { data: { blockHash, blockNumber } } = job;
|
||||
|
||||
// Get the current IPLD Status.
|
||||
const ipldStatus = await this._indexer.getIPLDStatus();
|
||||
assert(ipldStatus);
|
||||
|
||||
if (ipldStatus.latestCheckpointBlockNumber >= 0) {
|
||||
if (ipldStatus.latestCheckpointBlockNumber < (blockNumber - 1)) {
|
||||
// Create a checkpoint job for parent block.
|
||||
const [parentBlock] = await this._indexer.getBlocksAtHeight(blockNumber - 1, false);
|
||||
await this.createCheckpointJob(parentBlock.blockHash, parentBlock.blockNumber);
|
||||
|
||||
const message = `Checkpoints for blockNumber ${blockNumber - 1} not processed yet, aborting`;
|
||||
log(message);
|
||||
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
if (ipldStatus.latestCheckpointBlockNumber > (blockNumber - 1)) {
|
||||
log(`Checkpoints for blockNumber ${blockNumber} already processed`);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Process checkpoints for the given block.
|
||||
await this._indexer.processCheckpoint(blockHash);
|
||||
|
||||
// Update the IPLD status.
|
||||
await this._indexer.updateIPLDStatusCheckpointBlock(blockNumber);
|
||||
|
||||
// Create an IPFS job after completion of a checkpoint job.
|
||||
if (this._indexer.isIPFSConfigured()) {
|
||||
await this.createIPFSPutJob(blockHash, blockNumber);
|
||||
}
|
||||
|
||||
await this._jobQueue.markComplete(job);
|
||||
await this._baseJobRunner.processCheckpoint(job);
|
||||
});
|
||||
}
|
||||
|
||||
async subscribeIPFSQueue (): Promise<void> {
|
||||
await this._jobQueue.subscribe(QUEUE_IPFS, async (job) => {
|
||||
const { data: { blockHash, blockNumber } } = job;
|
||||
|
||||
const ipldStatus = await this._indexer.getIPLDStatus();
|
||||
assert(ipldStatus);
|
||||
|
||||
if (ipldStatus.latestIPFSBlockNumber >= 0) {
|
||||
if (ipldStatus.latestIPFSBlockNumber < (blockNumber - 1)) {
|
||||
// Create a IPFS job for parent block.
|
||||
const [parentBlock] = await this._indexer.getBlocksAtHeight(blockNumber - 1, false);
|
||||
await this.createIPFSPutJob(parentBlock.blockHash, parentBlock.blockNumber);
|
||||
|
||||
const message = `IPFS for blockNumber ${blockNumber - 1} not processed yet, aborting`;
|
||||
log(message);
|
||||
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
if (ipldStatus.latestIPFSBlockNumber > (blockNumber - 1)) {
|
||||
log(`IPFS for blockNumber ${blockNumber} already processed`);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Get IPLDBlocks for the given blocHash.
|
||||
const ipldBlocks = await this._indexer.getIPLDBlocksByHash(blockHash);
|
||||
|
||||
// Push all the IPLDBlocks to IPFS.
|
||||
for (const ipldBlock of ipldBlocks) {
|
||||
const data = this._indexer.getIPLDData(ipldBlock);
|
||||
await this._indexer.pushToIPFS(data);
|
||||
}
|
||||
|
||||
// Update the IPLD status.
|
||||
await this._indexer.updateIPLDStatusIPFSBlock(blockNumber);
|
||||
|
||||
await this._jobQueue.markComplete(job);
|
||||
});
|
||||
}
|
||||
|
||||
async createHooksJob (blockHash?: string, blockNumber?: number): Promise<void> {
|
||||
if (!blockNumber || !blockHash) {
|
||||
// Get the latest canonical block
|
||||
const latestCanonicalBlock = await this._indexer.getLatestCanonicalBlock();
|
||||
|
||||
// Create a hooks job for parent block of latestCanonicalBlock because pruning for first block is skipped as it is assumed to be a canonical block.
|
||||
blockHash = latestCanonicalBlock.parentHash;
|
||||
blockNumber = latestCanonicalBlock.blockNumber - 1;
|
||||
}
|
||||
|
||||
await this._jobQueue.pushJob(
|
||||
QUEUE_HOOKS,
|
||||
{
|
||||
blockHash,
|
||||
blockNumber
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async createCheckpointJob (blockHash: string, blockNumber: number): Promise<void> {
|
||||
await this._jobQueue.pushJob(
|
||||
QUEUE_BLOCK_CHECKPOINT,
|
||||
{
|
||||
blockHash,
|
||||
blockNumber
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async createIPFSPutJob (blockHash: string, blockNumber: number): Promise<void> {
|
||||
await this._jobQueue.pushJob(
|
||||
QUEUE_IPFS,
|
||||
{
|
||||
blockHash,
|
||||
blockNumber
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const main = async (): Promise<any> => {
|
||||
|
||||
@@ -8,12 +8,6 @@
|
||||
yarn
|
||||
```
|
||||
|
||||
* Run the IPFS (go-ipfs version 0.12.2) daemon:
|
||||
|
||||
```bash
|
||||
ipfs daemon
|
||||
```
|
||||
|
||||
* Create a postgres12 database for the watcher:
|
||||
|
||||
```bash
|
||||
@@ -47,7 +41,7 @@
|
||||
|
||||
* Update the `upstream` config and provide the `ipld-eth-server` GQL API endpoint.
|
||||
|
||||
* Update the `server` config with state checkpoint settings and provide the IPFS API address.
|
||||
* Update the `server` config with state checkpoint settings.
|
||||
|
||||
## Customize
|
||||
|
||||
@@ -59,11 +53,11 @@
|
||||
|
||||
* Generating state:
|
||||
|
||||
* Edit the custom hook function `createInitialState` (triggered if the watcher passes the start block, checkpoint: `true`) in [hooks.ts](./src/hooks.ts) to save an initial state `IPLDBlock` using the `Indexer` object.
|
||||
* Edit the custom hook function `createInitialState` (triggered if the watcher passes the start block, checkpoint: `true`) in [hooks.ts](./src/hooks.ts) to save an initial `State` using the `Indexer` object.
|
||||
|
||||
* Edit the custom hook function `createStateDiff` (triggered on a block) in [hooks.ts](./src/hooks.ts) to save the state in a `diff` `IPLDBlock` using the `Indexer` object. The default state (if exists) is updated.
|
||||
* Edit the custom hook function `createStateDiff` (triggered on a block) in [hooks.ts](./src/hooks.ts) to save the state in a `diff` `State` using the `Indexer` object. The default state (if exists) is updated.
|
||||
|
||||
* Edit the custom hook function `createStateCheckpoint` (triggered just before default and CLI checkpoint) in [hooks.ts](./src/hooks.ts) to save the state in a `checkpoint` `IPLDBlock` using the `Indexer` object.
|
||||
* Edit the custom hook function `createStateCheckpoint` (triggered just before default and CLI checkpoint) in [hooks.ts](./src/hooks.ts) to save the state in a `checkpoint` `State` using the `Indexer` object.
|
||||
|
||||
## Run
|
||||
|
||||
@@ -134,14 +128,14 @@ GQL console: http://localhost:{{port}}/graphql
|
||||
```
|
||||
|
||||
`cid`: CID of the checkpoint for which to verify.
|
||||
|
||||
|
||||
{{/if}}
|
||||
* To reset the watcher to a previous block number:
|
||||
|
||||
* Reset state:
|
||||
* Reset watcher:
|
||||
|
||||
```bash
|
||||
yarn reset state --block-number <previous-block-number>
|
||||
yarn reset watcher --block-number <previous-block-number>
|
||||
```
|
||||
|
||||
* Reset job-queue:
|
||||
@@ -150,6 +144,12 @@ GQL console: http://localhost:{{port}}/graphql
|
||||
yarn reset job-queue --block-number <previous-block-number>
|
||||
```
|
||||
|
||||
* Reset state:
|
||||
|
||||
```bash
|
||||
yarn reset state --block-number <previous-block-number>
|
||||
```
|
||||
|
||||
* `block-number`: Block number to which to reset the watcher.
|
||||
|
||||
* To export and import the watcher state:
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
//
|
||||
// Copyright 2022 Vulcanize, Inc.
|
||||
//
|
||||
|
||||
import debug from 'debug';
|
||||
|
||||
import { getConfig } from '@cerc-io/util';
|
||||
|
||||
import { Database } from '../../database';
|
||||
|
||||
const log = debug('vulcanize:reset-ipld-state');
|
||||
|
||||
export const command = 'ipld-state';
|
||||
|
||||
export const desc = 'Reset IPLD state in the given range';
|
||||
|
||||
export const builder = {
|
||||
blockNumber: {
|
||||
type: 'number'
|
||||
}
|
||||
};
|
||||
|
||||
export const handler = async (argv: any): Promise<void> => {
|
||||
const { blockNumber } = argv;
|
||||
const config = await getConfig(argv.configFile);
|
||||
|
||||
// Initialize database
|
||||
const db = new Database(config.database);
|
||||
await db.init();
|
||||
|
||||
// Create a DB transaction
|
||||
const dbTx = await db.createTransactionRunner();
|
||||
|
||||
console.time('time:reset-ipld-state');
|
||||
try {
|
||||
// Delete all IPLDBlock entries in the given range
|
||||
await db.removeIPLDBlocksAfterBlock(dbTx, blockNumber);
|
||||
|
||||
// Reset the IPLD status.
|
||||
const ipldStatus = await db.getIPLDStatus();
|
||||
|
||||
if (ipldStatus) {
|
||||
if (ipldStatus.latestHooksBlockNumber > blockNumber) {
|
||||
await db.updateIPLDStatusHooksBlock(dbTx, blockNumber, true);
|
||||
}
|
||||
|
||||
if (ipldStatus.latestCheckpointBlockNumber > blockNumber) {
|
||||
await db.updateIPLDStatusCheckpointBlock(dbTx, blockNumber, true);
|
||||
}
|
||||
|
||||
if (ipldStatus.latestIPFSBlockNumber > blockNumber) {
|
||||
await db.updateIPLDStatusIPFSBlock(dbTx, blockNumber, true);
|
||||
}
|
||||
}
|
||||
|
||||
dbTx.commitTransaction();
|
||||
} catch (error) {
|
||||
await dbTx.rollbackTransaction();
|
||||
throw error;
|
||||
} finally {
|
||||
await dbTx.release();
|
||||
}
|
||||
console.timeEnd('time:reset-ipld-state');
|
||||
|
||||
log(`Reset ipld-state successfully to block ${blockNumber}`);
|
||||
};
|
||||
@@ -1,32 +1,18 @@
|
||||
//
|
||||
// Copyright 2021 Vulcanize, Inc.
|
||||
// Copyright 2022 Vulcanize, Inc.
|
||||
//
|
||||
|
||||
{{#if (subgraphPath)}}
|
||||
import path from 'path';
|
||||
{{/if}}
|
||||
import debug from 'debug';
|
||||
import { MoreThan } from 'typeorm';
|
||||
import assert from 'assert';
|
||||
|
||||
import { getConfig, initClients, resetJobs, JobQueue } from '@cerc-io/util';
|
||||
{{#if (subgraphPath)}}
|
||||
import { GraphWatcher, Database as GraphDatabase } from '@cerc-io/graph-node';
|
||||
{{/if}}
|
||||
import { getConfig } from '@cerc-io/util';
|
||||
|
||||
import { Database } from '../../database';
|
||||
import { Indexer } from '../../indexer';
|
||||
import { BlockProgress } from '../../entity/BlockProgress';
|
||||
|
||||
{{#each queries as | query |}}
|
||||
import { {{query.entityName}} } from '../../entity/{{query.entityName}}';
|
||||
{{/each}}
|
||||
|
||||
const log = debug('vulcanize:reset-state');
|
||||
|
||||
export const command = 'state';
|
||||
|
||||
export const desc = 'Reset state to block number';
|
||||
export const desc = 'Reset State to a given block number';
|
||||
|
||||
export const builder = {
|
||||
blockNumber: {
|
||||
@@ -35,87 +21,34 @@ export const builder = {
|
||||
};
|
||||
|
||||
export const handler = async (argv: any): Promise<void> => {
|
||||
const { blockNumber } = argv;
|
||||
const config = await getConfig(argv.configFile);
|
||||
await resetJobs(config);
|
||||
const { ethClient, ethProvider } = await initClients(config);
|
||||
|
||||
// Initialize database.
|
||||
// Initialize database
|
||||
const db = new Database(config.database);
|
||||
await db.init();
|
||||
{{#if (subgraphPath)}}
|
||||
|
||||
const graphDb = new GraphDatabase(config.database, path.resolve(__dirname, '../../entity/*'));
|
||||
await graphDb.init();
|
||||
|
||||
const graphWatcher = new GraphWatcher(graphDb, ethClient, ethProvider, config.server);
|
||||
{{/if}}
|
||||
|
||||
const jobQueueConfig = config.jobQueue;
|
||||
assert(jobQueueConfig, 'Missing job queue config');
|
||||
|
||||
const { dbConnectionString, maxCompletionLagInSecs } = jobQueueConfig;
|
||||
assert(dbConnectionString, 'Missing job queue db connection string');
|
||||
|
||||
const jobQueue = new JobQueue({ dbConnectionString, maxCompletionLag: maxCompletionLagInSecs });
|
||||
await jobQueue.start();
|
||||
|
||||
const indexer = new Indexer(config.server, db, ethClient, ethProvider, jobQueue{{#if (subgraphPath)}}, graphWatcher{{/if}});
|
||||
await indexer.init();
|
||||
{{#if (subgraphPath)}}
|
||||
|
||||
graphWatcher.setIndexer(indexer);
|
||||
await graphWatcher.init();
|
||||
{{/if}}
|
||||
|
||||
const blockProgresses = await indexer.getBlocksAtHeight(argv.blockNumber, false);
|
||||
assert(blockProgresses.length, `No blocks at specified block number ${argv.blockNumber}`);
|
||||
assert(!blockProgresses.some(block => !block.isComplete), `Incomplete block at block number ${argv.blockNumber} with unprocessed events`);
|
||||
const [blockProgress] = blockProgresses;
|
||||
|
||||
// Create a DB transaction
|
||||
const dbTx = await db.createTransactionRunner();
|
||||
|
||||
console.time('time:reset-state');
|
||||
try {
|
||||
const entities = [BlockProgress
|
||||
{{~#each queries as | query |~}}
|
||||
, {{query.entityName}}
|
||||
{{~/each~}}
|
||||
];
|
||||
// Delete all State entries in the given range
|
||||
await db.removeStatesAfterBlock(dbTx, blockNumber);
|
||||
|
||||
const removeEntitiesPromise = entities.map(async entityClass => {
|
||||
return db.removeEntities<any>(dbTx, entityClass, { blockNumber: MoreThan(argv.blockNumber) });
|
||||
});
|
||||
// Reset the stateSyncStatus.
|
||||
const stateSyncStatus = await db.getStateSyncStatus();
|
||||
|
||||
await Promise.all(removeEntitiesPromise);
|
||||
|
||||
const syncStatus = await indexer.getSyncStatus();
|
||||
assert(syncStatus, 'Missing syncStatus');
|
||||
|
||||
if (syncStatus.latestIndexedBlockNumber > blockProgress.blockNumber) {
|
||||
await indexer.updateSyncStatusIndexedBlock(blockProgress.blockHash, blockProgress.blockNumber, true);
|
||||
}
|
||||
|
||||
if (syncStatus.latestCanonicalBlockNumber > blockProgress.blockNumber) {
|
||||
await indexer.updateSyncStatusCanonicalBlock(blockProgress.blockHash, blockProgress.blockNumber, true);
|
||||
}
|
||||
|
||||
const ipldStatus = await indexer.getIPLDStatus();
|
||||
|
||||
if (ipldStatus) {
|
||||
if (ipldStatus.latestHooksBlockNumber > blockProgress.blockNumber) {
|
||||
await indexer.updateIPLDStatusHooksBlock(blockProgress.blockNumber, true);
|
||||
if (stateSyncStatus) {
|
||||
if (stateSyncStatus.latestIndexedBlockNumber > blockNumber) {
|
||||
await db.updateStateSyncStatusIndexedBlock(dbTx, blockNumber, true);
|
||||
}
|
||||
|
||||
if (ipldStatus.latestCheckpointBlockNumber > blockProgress.blockNumber) {
|
||||
await indexer.updateIPLDStatusCheckpointBlock(blockProgress.blockNumber, true);
|
||||
}
|
||||
|
||||
if (ipldStatus.latestIPFSBlockNumber > blockProgress.blockNumber) {
|
||||
await indexer.updateIPLDStatusIPFSBlock(blockProgress.blockNumber, true);
|
||||
if (stateSyncStatus.latestCheckpointBlockNumber > blockNumber) {
|
||||
await db.updateStateSyncStatusCheckpointBlock(dbTx, blockNumber, true);
|
||||
}
|
||||
}
|
||||
|
||||
await indexer.updateSyncStatusChainHead(blockProgress.blockHash, blockProgress.blockNumber, true);
|
||||
|
||||
dbTx.commitTransaction();
|
||||
} catch (error) {
|
||||
await dbTx.rollbackTransaction();
|
||||
@@ -123,6 +56,7 @@ export const handler = async (argv: any): Promise<void> => {
|
||||
} finally {
|
||||
await dbTx.release();
|
||||
}
|
||||
console.timeEnd('time:reset-state');
|
||||
|
||||
log('Reset state successfully');
|
||||
log(`Reset State successfully to block ${blockNumber}`);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
//
|
||||
// Copyright 2021 Vulcanize, Inc.
|
||||
//
|
||||
|
||||
{{#if (subgraphPath)}}
|
||||
import path from 'path';
|
||||
{{/if}}
|
||||
import debug from 'debug';
|
||||
import { MoreThan } from 'typeorm';
|
||||
import assert from 'assert';
|
||||
|
||||
import { getConfig, initClients, resetJobs, JobQueue } from '@cerc-io/util';
|
||||
{{#if (subgraphPath)}}
|
||||
import { GraphWatcher, Database as GraphDatabase } from '@cerc-io/graph-node';
|
||||
{{/if}}
|
||||
|
||||
import { Database } from '../../database';
|
||||
import { Indexer } from '../../indexer';
|
||||
import { BlockProgress } from '../../entity/BlockProgress';
|
||||
|
||||
{{#each queries as | query |}}
|
||||
import { {{query.entityName}} } from '../../entity/{{query.entityName}}';
|
||||
{{/each}}
|
||||
|
||||
const log = debug('vulcanize:reset-watcher');
|
||||
|
||||
export const command = 'watcher';
|
||||
|
||||
export const desc = 'Reset watcher to a block number';
|
||||
|
||||
export const builder = {
|
||||
blockNumber: {
|
||||
type: 'number'
|
||||
}
|
||||
};
|
||||
|
||||
export const handler = async (argv: any): Promise<void> => {
|
||||
const config = await getConfig(argv.configFile);
|
||||
await resetJobs(config);
|
||||
const { ethClient, ethProvider } = await initClients(config);
|
||||
|
||||
// Initialize database.
|
||||
const db = new Database(config.database);
|
||||
await db.init();
|
||||
{{#if (subgraphPath)}}
|
||||
|
||||
const graphDb = new GraphDatabase(config.database, path.resolve(__dirname, '../../entity/*'));
|
||||
await graphDb.init();
|
||||
|
||||
const graphWatcher = new GraphWatcher(graphDb, ethClient, ethProvider, config.server);
|
||||
{{/if}}
|
||||
|
||||
const jobQueueConfig = config.jobQueue;
|
||||
assert(jobQueueConfig, 'Missing job queue config');
|
||||
|
||||
const { dbConnectionString, maxCompletionLagInSecs } = jobQueueConfig;
|
||||
assert(dbConnectionString, 'Missing job queue db connection string');
|
||||
|
||||
const jobQueue = new JobQueue({ dbConnectionString, maxCompletionLag: maxCompletionLagInSecs });
|
||||
await jobQueue.start();
|
||||
|
||||
const indexer = new Indexer(config.server, db, ethClient, ethProvider, jobQueue{{#if (subgraphPath)}}, graphWatcher{{/if}});
|
||||
await indexer.init();
|
||||
{{#if (subgraphPath)}}
|
||||
|
||||
graphWatcher.setIndexer(indexer);
|
||||
await graphWatcher.init();
|
||||
{{/if}}
|
||||
|
||||
const blockProgresses = await indexer.getBlocksAtHeight(argv.blockNumber, false);
|
||||
assert(blockProgresses.length, `No blocks at specified block number ${argv.blockNumber}`);
|
||||
assert(!blockProgresses.some(block => !block.isComplete), `Incomplete block at block number ${argv.blockNumber} with unprocessed events`);
|
||||
const [blockProgress] = blockProgresses;
|
||||
|
||||
const dbTx = await db.createTransactionRunner();
|
||||
|
||||
try {
|
||||
const entities = [BlockProgress
|
||||
{{~#each queries as | query |~}}
|
||||
, {{query.entityName}}
|
||||
{{~/each~}}
|
||||
];
|
||||
|
||||
const removeEntitiesPromise = entities.map(async entityClass => {
|
||||
return db.removeEntities<any>(dbTx, entityClass, { blockNumber: MoreThan(argv.blockNumber) });
|
||||
});
|
||||
|
||||
await Promise.all(removeEntitiesPromise);
|
||||
|
||||
const syncStatus = await indexer.getSyncStatus();
|
||||
assert(syncStatus, 'Missing syncStatus');
|
||||
|
||||
if (syncStatus.latestIndexedBlockNumber > blockProgress.blockNumber) {
|
||||
await indexer.updateSyncStatusIndexedBlock(blockProgress.blockHash, blockProgress.blockNumber, true);
|
||||
}
|
||||
|
||||
if (syncStatus.latestCanonicalBlockNumber > blockProgress.blockNumber) {
|
||||
await indexer.updateSyncStatusCanonicalBlock(blockProgress.blockHash, blockProgress.blockNumber, true);
|
||||
}
|
||||
|
||||
const stateSyncStatus = await indexer.getStateSyncStatus();
|
||||
|
||||
if (stateSyncStatus) {
|
||||
if (stateSyncStatus.latestIndexedBlockNumber > blockProgress.blockNumber) {
|
||||
await indexer.updateStateSyncStatusIndexedBlock(blockProgress.blockNumber, true);
|
||||
}
|
||||
|
||||
if (stateSyncStatus.latestCheckpointBlockNumber > blockProgress.blockNumber) {
|
||||
await indexer.updateStateSyncStatusCheckpointBlock(blockProgress.blockNumber, true);
|
||||
}
|
||||
}
|
||||
|
||||
await indexer.updateSyncStatusChainHead(blockProgress.blockHash, blockProgress.blockNumber, true);
|
||||
|
||||
dbTx.commitTransaction();
|
||||
} catch (error) {
|
||||
await dbTx.rollbackTransaction();
|
||||
throw error;
|
||||
} finally {
|
||||
await dbTx.release();
|
||||
}
|
||||
|
||||
log('Reset watcher successfully');
|
||||
};
|
||||
@@ -8,7 +8,7 @@ import debug from 'debug';
|
||||
import Decimal from 'decimal.js';
|
||||
import { GraphQLResolveInfo, GraphQLScalarType } from 'graphql';
|
||||
|
||||
import { ValueResult, BlockHeight, gqlTotalQueryCount, gqlQueryCount, jsonBigIntStringReplacer } from '@cerc-io/util';
|
||||
import { ValueResult, BlockHeight, gqlTotalQueryCount, gqlQueryCount, jsonBigIntStringReplacer, getResultState } from '@cerc-io/util';
|
||||
|
||||
import { Indexer } from './indexer';
|
||||
import { EventWatcher } from './events';
|
||||
@@ -126,9 +126,9 @@ export const createResolvers = async (indexer: Indexer, eventWatcher: EventWatch
|
||||
gqlTotalQueryCount.inc(1);
|
||||
gqlQueryCount.labels('getStateByCID').inc(1);
|
||||
|
||||
const ipldBlock = await indexer.getIPLDBlockByCid(cid);
|
||||
const state = await indexer.getStateByCID(cid);
|
||||
|
||||
return ipldBlock && ipldBlock.block.isComplete ? indexer.getResultIPLDBlock(ipldBlock) : undefined;
|
||||
return state && state.block.isComplete ? getResultState(state) : undefined;
|
||||
},
|
||||
|
||||
getState: async (_: any, { blockHash, contractAddress, kind }: { blockHash: string, contractAddress: string, kind: string }) => {
|
||||
@@ -136,9 +136,9 @@ export const createResolvers = async (indexer: Indexer, eventWatcher: EventWatch
|
||||
gqlTotalQueryCount.inc(1);
|
||||
gqlQueryCount.labels('getState').inc(1);
|
||||
|
||||
const ipldBlock = await indexer.getPrevIPLDBlock(blockHash, contractAddress, kind);
|
||||
const state = await indexer.getPrevState(blockHash, contractAddress, kind);
|
||||
|
||||
return ipldBlock && ipldBlock.block.isComplete ? indexer.getResultIPLDBlock(ipldBlock) : undefined;
|
||||
return state && state.block.isComplete ? getResultState(state) : undefined;
|
||||
},
|
||||
|
||||
getSyncStatus: async () => {
|
||||
|
||||
@@ -210,13 +210,14 @@ export class Visitor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the reset.ts, job-queue.ts, state.ts files generated from templates to respective streams.
|
||||
* Writes the reset.ts, job-queue.ts, watcher.ts, state.ts files generated from templates to respective streams.
|
||||
* @param resetOutStream A writable output stream to write the reset file to.
|
||||
* @param resetJQOutStream A writable output stream to write the reset job-queue file to.
|
||||
* @param resetWatcherOutStream A writable output stream to write the reset watcher file to.
|
||||
* @param resetStateOutStream A writable output stream to write the reset state file to.
|
||||
*/
|
||||
exportReset (resetOutStream: Writable, resetJQOutStream: Writable, resetStateOutStream: Writable, resetIPLDStateOutStream: Writable | undefined, subgraphPath: string): void {
|
||||
this._reset.exportReset(resetOutStream, resetJQOutStream, resetStateOutStream, resetIPLDStateOutStream, subgraphPath);
|
||||
exportReset (resetOutStream: Writable, resetJQOutStream: Writable, resetWatcherOutStream: Writable, resetStateOutStream: Writable, subgraphPath: string): void {
|
||||
this._reset.exportReset(resetOutStream, resetJQOutStream, resetWatcherOutStream, resetStateOutStream, subgraphPath);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user