chain: add interactive scan.
This commit is contained in:
parent
9659699d51
commit
27ec4bfa51
4 changed files with 146 additions and 1 deletions
|
|
@ -28,6 +28,7 @@ const {OwnershipProof} = require('../covenants/ownership');
|
|||
const AirdropProof = require('../primitives/airdropproof');
|
||||
const {CriticalError} = require('../errors');
|
||||
const thresholdStates = common.thresholdStates;
|
||||
const scanAction = common.scanAction;
|
||||
const {states} = NameState;
|
||||
|
||||
const {
|
||||
|
|
@ -2250,7 +2251,7 @@ class Chain extends AsyncEmitter {
|
|||
|
||||
/**
|
||||
* Scan the blockchain for transactions containing specified address hashes.
|
||||
* @param {Hash} start - Block hash to start at.
|
||||
* @param {Hash|Number} start - Block hash or height to start at.
|
||||
* @param {Bloom} filter - Bloom filter containing tx and address hashes.
|
||||
* @param {Function} iter - Iterator.
|
||||
* @returns {Promise}
|
||||
|
|
@ -2265,6 +2266,78 @@ class Chain extends AsyncEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interactive scan the blockchain for transactions containing specified
|
||||
* address hashes. Allows repeat and abort.
|
||||
* @param {Hash|Number} start - Block hash or height to start at.
|
||||
* @param {BloomFilter} filter - Starting bloom filter containing tx,
|
||||
* address and name hashes.
|
||||
* @param {Function} iter - Iterator.
|
||||
*/
|
||||
|
||||
async scanInteractive(start, filter, iter) {
|
||||
if (start == null)
|
||||
start = this.network.genesis.hash;
|
||||
|
||||
if (typeof start === 'number')
|
||||
this.logger.info('Scanning(interactive) from height %d.', start);
|
||||
else
|
||||
this.logger.info('Scanning(interactive) from block %x.', start);
|
||||
|
||||
let hash = start;
|
||||
|
||||
while (hash != null) {
|
||||
const unlock = await this.locker.lock();
|
||||
|
||||
try {
|
||||
const {entry, txs} = await this.db.scanBlock(hash, filter);
|
||||
|
||||
const action = await iter(entry, txs);
|
||||
|
||||
if (!action || typeof action !== 'object')
|
||||
throw new Error('Did not get proper action');
|
||||
|
||||
switch (action.type) {
|
||||
case scanAction.REPEAT: {
|
||||
break;
|
||||
}
|
||||
case scanAction.REPEAT_SET: {
|
||||
// try again with updated filter.
|
||||
filter = action.filter;
|
||||
break;
|
||||
}
|
||||
case scanAction.REPEAT_ADD: {
|
||||
if (!filter)
|
||||
throw new Error('No filter set.');
|
||||
|
||||
for (const chunk of action.chunks)
|
||||
filter.add(chunk);
|
||||
break;
|
||||
}
|
||||
case scanAction.NEXT: {
|
||||
const next = await this.getNext(entry);
|
||||
hash = next && next.hash;
|
||||
break;
|
||||
}
|
||||
case scanAction.ABORT: {
|
||||
this.logger.info('Scan(interactive) aborted at %x (%d).',
|
||||
entry.hash, entry.height);
|
||||
throw new Error('scan request aborted.');
|
||||
}
|
||||
default:
|
||||
this.logger.debug('Scan(interactive) aborting. Unknown action: %d',
|
||||
action.type);
|
||||
throw new Error('Unknown action.');
|
||||
}
|
||||
} catch (e) {
|
||||
this.logger.debug('Scan(interactive) errored. Error: %s', e.message);
|
||||
throw e;
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a block to the chain, perform all necessary verification.
|
||||
* @param {Block} block
|
||||
|
|
|
|||
|
|
@ -1612,6 +1612,51 @@ class ChainDB {
|
|||
this.logger.info('Finished scanning %d blocks.', total);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interactive scans block checks.
|
||||
* @param {Hash|Number} blockID - Block hash or height to start at.
|
||||
* @param {BloomFilter} [filter] - Starting bloom filter containing tx,
|
||||
* address and name hashes.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
async scanBlock(blockID, filter) {
|
||||
assert(blockID);
|
||||
|
||||
const entry = await this.getEntry(blockID);
|
||||
|
||||
if (!entry)
|
||||
throw new Error('Could not find entry.');
|
||||
|
||||
if (!await this.isMainChain(entry))
|
||||
throw new Error('Cannot rescan an alternate chain.');
|
||||
|
||||
const block = await this.getBlock(entry.hash);
|
||||
|
||||
if (!block)
|
||||
throw new Error('Block not found.');
|
||||
|
||||
this.logger.info(
|
||||
'Scanning block %x (%d)',
|
||||
entry.hash, entry.height);
|
||||
|
||||
let txs = [];
|
||||
|
||||
if (!filter) {
|
||||
txs = block.txs;
|
||||
} else {
|
||||
for (const tx of block.txs) {
|
||||
if (tx.testAndMaybeUpdate(filter))
|
||||
txs.push(tx);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
entry,
|
||||
txs
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Save an entry to the database and optionally
|
||||
* connect it as the tip. Note that this method
|
||||
|
|
|
|||
|
|
@ -69,3 +69,18 @@ exports.flags = {
|
|||
exports.flags.DEFAULT_FLAGS = 0
|
||||
| exports.flags.VERIFY_POW
|
||||
| exports.flags.VERIFY_BODY;
|
||||
|
||||
/**
|
||||
* Interactive scan actions.
|
||||
* @enum {Number}
|
||||
* @default
|
||||
*/
|
||||
|
||||
exports.scanAction = {
|
||||
NONE: 0,
|
||||
ABORT: 1,
|
||||
NEXT: 2,
|
||||
REPEAT_SET: 3,
|
||||
REPEAT_ADD: 4,
|
||||
REPEAT: 5
|
||||
};
|
||||
|
|
|
|||
|
|
@ -364,6 +364,18 @@ class FullNode extends Node {
|
|||
return this.chain.scan(start, filter, iter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interactive rescan for any missed transactions.
|
||||
* @param {Number|Hash} start - Start block.
|
||||
* @param {Bloom} filter
|
||||
* @param {Function} iter - Iterator.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
scanInteractive(start, filter, iter) {
|
||||
return this.chain.scanInteractive(start, filter, iter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast a transaction.
|
||||
* @param {TX|Block|Claim|AirdropProof} item
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue