chain: add interactive scan.

This commit is contained in:
Nodari Chkuaselidze 2023-10-11 19:54:11 +04:00
parent 9659699d51
commit 27ec4bfa51
No known key found for this signature in database
GPG key ID: B018A7BB437D1F05
4 changed files with 146 additions and 1 deletions

View file

@ -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

View file

@ -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

View file

@ -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
};

View file

@ -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