fcoin/lib/indexer/chainclient.js
Javed Khan 05794f5cb3
indexer: add module indexer
module indexer introduces a extensible architecture for indexing the
chain. It provides a base class which handles syncing with the chain,
handling re-orgs, interruptions, dynamic toggling, etc. TXIndexer
and AddrIndexer are provided for indexing transactions and addresses,
using the same flags as before i.e --index-tx and --index-address.
Indexes are stored in a different database and can be maintained
independently of the chain.
2019-05-15 12:02:41 -07:00

201 lines
3.6 KiB
JavaScript

/*!
* chainclient.js - chain client for bcoin
* Copyright (c) 2018, the bcoin developers (MIT License).
* https://github.com/bcoin-org/bcoin
*/
'use strict';
const assert = require('assert');
const AsyncEmitter = require('bevent');
const Chain = require('../blockchain/chain');
/**
* Chain Client
* @extends AsyncEmitter
* @alias module:indexer.ChainClient
*/
class ChainClient extends AsyncEmitter {
/**
* Create a chain client.
* @constructor
* @param {Chain} chain
*/
constructor(chain) {
super();
assert(chain instanceof Chain);
this.chain = chain;
this.network = chain.network;
this.opened = false;
this.init();
}
/**
* Initialize the client.
*/
init() {
this.chain.on('connect', async (entry, block, view) => {
if (!this.opened)
return;
await this.emitAsync('block connect', entry, block, view);
});
this.chain.on('disconnect', async (entry, block, view) => {
if (!this.opened)
return;
await this.emitAsync('block disconnect', entry, block, view);
});
this.chain.on('reset', async (tip) => {
if (!this.opened)
return;
await this.emitAsync('chain reset', tip);
});
}
/**
* Open the client.
* @returns {Promise}
*/
async open(options) {
assert(!this.opened, 'ChainClient is already open.');
this.opened = true;
setImmediate(() => this.emit('connect'));
}
/**
* Close the client.
* @returns {Promise}
*/
async close() {
assert(this.opened, 'ChainClient is not open.');
this.opened = false;
setImmediate(() => this.emit('disconnect'));
}
/**
* Get chain tip.
* @returns {Promise}
*/
async getTip() {
return this.chain.tip;
}
/**
* Get chain entry.
* @param {Hash} hash
* @returns {Promise} - Returns {@link ChainEntry}.
*/
async getEntry(hash) {
const entry = await this.chain.getEntry(hash);
if (!entry)
return null;
if (!await this.chain.isMainChain(entry))
return null;
return entry;
}
/**
* Get a coin (unspents only).
* @param {Hash} hash
* @param {Number} index
* @returns {Promise} - Returns {@link Coin}.
*/
async getCoin(hash, index) {
return this.chain.getCoin(hash, index);
}
/**
* Get hash range.
* @param {Number} start
* @param {Number} end
* @returns {Promise}
*/
async getHashes(start = -1, end = -1) {
return this.chain.getHashes(start, end);
}
/**
* Get block
* @param {Hash} hash
* @returns {Promise} - Returns {@link Block}
*/
async getBlock(hash) {
const block = await this.chain.getBlock(hash);
if (!block)
return null;
return block;
}
/**
* Get a historical block coin viewpoint.
* @param {Block} hash
* @returns {Promise} - Returns {@link CoinView}.
*/
async getBlockView(block) {
return this.chain.getBlockView(block);
}
/**
* Get coin viewpoint.
* @param {TX} tx
* @returns {Promise} - Returns {@link CoinView}.
*/
async getCoinView(tx) {
return this.chain.getCoinView(tx);
}
/**
* Rescan for any missed blocks.
* @param {Number} start - Start block.
* @returns {Promise}
*/
async rescan(start) {
for (let i = start; ; i++) {
const entry = await this.getEntry(i);
if (!entry) {
await this.emitAsync('chain tip');
break;
};
const block = await this.getBlock(entry.hash);
assert(block);
const view = await this.getBlockView(block);
assert(view);
await this.emitAsync('block rescan', entry, block, view);
}
};
}
/*
* Expose
*/
module.exports = ChainClient;