diff --git a/bin/cli b/bin/cli index 88196aea..9dc8b67d 100755 --- a/bin/cli +++ b/bin/cli @@ -430,7 +430,7 @@ CLI.prototype.retoken = async function retoken() { CLI.prototype.rescan = async function rescan() { const height = this.config.uint(0); - await this.client.rescan(height); + await this.wallet.rescan(height); this.log('Rescanning...'); }; @@ -658,6 +658,9 @@ CLI.prototype.handleWallet = async function handleWallet() { case 'resend': await this.resendWallet(); break; + case 'rescan': + await this.rescan(); + break; default: this.log('Unrecognized command.'); this.log('Commands:'); @@ -692,6 +695,7 @@ CLI.prototype.handleWallet = async function handleWallet() { this.log(' $ lock: Lock wallet.'); this.log(' $ unlock [passphrase] [timeout?]: Unlock wallet.'); this.log(' $ resend: Resend pending transactions.'); + this.log(' $ rescan [height]: Rescan for transactions.'); this.log('Other Options:'); this.log(' --passphrase [passphrase]: For signing & account creation.'); this.log(' --account [account-name]: Account name.'); @@ -734,9 +738,6 @@ CLI.prototype.handleNode = async function handleNode() { case 'block': await this.getBlock(); break; - case 'rescan': - await this.rescan(); - break; case 'reset': await this.reset(); break; @@ -760,7 +761,6 @@ CLI.prototype.handleNode = async function handleNode() { this.log(' $ tx [hash/address]: View transactions.'); this.log(' $ coin [hash+index/address]: View coins.'); this.log(' $ block [hash/height]: View block.'); - this.log(' $ rescan [height]: Rescan for transactions.'); this.log(' $ reset [height/hash]: Reset chain to desired block.'); this.log(' $ resend: Resend pending transactions.'); this.log(' $ backup [path]: Backup the wallet db.'); diff --git a/lib/blockchain/chain.js b/lib/blockchain/chain.js index f57caec9..a52be566 100644 --- a/lib/blockchain/chain.js +++ b/lib/blockchain/chain.js @@ -905,7 +905,7 @@ Chain.prototype.reorganize = async function reorganize(competitor) { competitor.height ); - await this.fire('reorganize', tip, competitor); + await this.call('reorganize', tip, competitor); }; /** @@ -942,7 +942,7 @@ Chain.prototype.reorganizeSPV = async function reorganizeSPV(competitor) { for (const entry of disconnect) { const headers = entry.toHeaders(); const view = new CoinView(); - await this.fire('disconnect', entry, headers, view); + await this.call('disconnect', entry, headers, view); } this.logger.warning( @@ -957,7 +957,7 @@ Chain.prototype.reorganizeSPV = async function reorganizeSPV(competitor) { 'Chain replay from height %d necessary.', fork.height); - await this.fire('reorganize', tip, competitor); + await this.call('reorganize', tip, competitor); }; /** @@ -985,7 +985,7 @@ Chain.prototype.disconnect = async function disconnect(entry) { this.emit('tip', prev); - await this.fire('disconnect', entry, block, view); + await this.call('disconnect', entry, block, view); }; /** @@ -1035,7 +1035,7 @@ Chain.prototype.reconnect = async function reconnect(entry) { this.emit('tip', entry); this.emit('reconnect', entry, block); - await this.fire('connect', entry, block, view); + await this.call('connect', entry, block, view); }; /** @@ -1103,7 +1103,7 @@ Chain.prototype.setBestChain = async function setBestChain(entry, block, prev, f this.emit('tip', entry); this.emit('block', block, entry); - await this.fire('connect', entry, block, view); + await this.call('connect', entry, block, view); }; /** @@ -1198,7 +1198,7 @@ Chain.prototype._reset = async function _reset(block, silent) { this.emit('tip', tip); if (!silent) - await this.fire('reset', tip); + await this.call('reset', tip); // Reset the orphan map completely. There may // have been some orphans on a forked chain we diff --git a/lib/blockchain/chaindb.js b/lib/blockchain/chaindb.js index 94e9d19a..a1c1d228 100644 --- a/lib/blockchain/chaindb.js +++ b/lib/blockchain/chaindb.js @@ -1307,7 +1307,7 @@ ChainDB.prototype.scan = async function scan(start, filter, iter) { const block = await this.getBlock(entry.hash); const txs = []; - total++; + total += 1; if (!block) { if (!this.options.spv && !this.options.prune) diff --git a/lib/node/http.js b/lib/node/http.js index 046fbd41..d5c54e79 100644 --- a/lib/node/http.js +++ b/lib/node/http.js @@ -498,7 +498,7 @@ class HTTP extends Server { const tx = TX.fromRaw(data); - this.node.send(tx); + this.node.relay(tx); return null; }); @@ -655,7 +655,7 @@ class HTTP extends Server { for (const tx of txs) raw.push(tx.toRaw()); - return socket.fire('block rescan', block, raw); + return socket.call('block rescan', block, raw); }); return null; } diff --git a/lib/utils/asyncemitter.js b/lib/utils/asyncemitter.js index a2c0cffd..39f241ea 100644 --- a/lib/utils/asyncemitter.js +++ b/lib/utils/asyncemitter.js @@ -209,6 +209,37 @@ AsyncEmitter.prototype.listenerCount = function listenerCount(type) { return listeners.length; }; +/** + * Add a listener. + * @param {String} type + * @param {Function} handler + */ + +AsyncEmitter.prototype.listen = function listen(type, handler) { + return this.on(type, handler); +}; + +/** + * Add a listener. + * @param {String} type + * @param {Function} handler + */ + +AsyncEmitter.prototype.hook = function hook(type, handler) { + return this.on(type, handler); +}; + +/** + * Emit event. + * @param {String} type + * @param {...Object} args + * @returns {Promise} + */ + +AsyncEmitter.prototype.fire = function fire() { + return this.emit.apply(this, arguments); +}; + /** * Emit an event synchronously. * @method @@ -280,7 +311,7 @@ AsyncEmitter.prototype.emit = function emit(type) { * @returns {Promise} */ -AsyncEmitter.prototype.fire = async function fire(type) { +AsyncEmitter.prototype.call = async function call(type) { assert(typeof type === 'string', '`type` must be a string.'); const listeners = this._events[type]; @@ -343,15 +374,15 @@ AsyncEmitter.prototype.fire = async function fire(type) { * @returns {Promise} */ -AsyncEmitter.prototype.tryFire = async function tryFire(type) { +AsyncEmitter.prototype.tryCall = async function tryCall(type) { try { - await this.fire.apply(this, arguments); + await this.call.apply(this, arguments); } catch (e) { if (type === 'error') return; try { - await this.fire('error', e); + await this.call('error', e); } catch (e) { ; } diff --git a/lib/utils/asyncobject.js b/lib/utils/asyncobject.js index 3e306f42..5c13eed9 100644 --- a/lib/utils/asyncobject.js +++ b/lib/utils/asyncobject.js @@ -61,7 +61,7 @@ AsyncObject.prototype.__open = async function __open() { if (this.loaded) return; - await this.fire('preopen'); + await this.call('preopen'); this.loading = true; @@ -76,7 +76,7 @@ AsyncObject.prototype.__open = async function __open() { this.loading = false; this.loaded = true; - await this.fire('open'); + await this.call('open'); }; /** @@ -105,7 +105,7 @@ AsyncObject.prototype.__close = async function __close() { if (!this.loaded) return; - await this.fire('preclose'); + await this.call('preclose'); this.closing = true; @@ -120,7 +120,7 @@ AsyncObject.prototype.__close = async function __close() { this.closing = false; this.loaded = false; - await this.fire('close'); + await this.call('close'); }; /** @@ -151,6 +151,27 @@ AsyncObject.prototype._close = function _close(callback) { throw new Error('Abstract method.'); }; +/** + * Add a listener. + * @param {String} type + * @param {Function} handler + */ + +AsyncObject.prototype.listen = function listen(type, handler) { + return this.on(type, handler); +}; + +/** + * Emit event. + * @param {String} type + * @param {...Object} args + * @returns {Promise} + */ + +AsyncObject.prototype.fire = function fire() { + return this.emit.apply(this, arguments); +}; + /** * Add a hook listener. * @param {String} type @@ -174,8 +195,8 @@ AsyncObject.prototype.hook = function hook(type, handler) { * @returns {Promise} */ -AsyncObject.prototype.fire = async function fire() { - await this.fireHook.apply(this, arguments); +AsyncObject.prototype.call = async function call() { + await this.callHook.apply(this, arguments); this.emit.apply(this, arguments); }; @@ -188,7 +209,7 @@ AsyncObject.prototype.fire = async function fire() { * @returns {Promise} */ -AsyncObject.prototype.fireHook = async function fireHook(type) { +AsyncObject.prototype.callHook = async function callHook(type) { assert(typeof type === 'string', '`type` must be a string.'); const listeners = this._hooks[type]; diff --git a/lib/utils/bloom.js b/lib/utils/bloom.js index 1452bc59..f228e2f1 100644 --- a/lib/utils/bloom.js +++ b/lib/utils/bloom.js @@ -335,6 +335,7 @@ Bloom.prototype.toRaw = function toRaw() { Bloom.prototype.fromReader = function fromReader(br) { this.filter = br.readVarBytes(); + this.size = this.filter.length * 8; this.n = br.readU32(); this.tweak = br.readU32(); this.update = br.readU8(); diff --git a/lib/wallet/client.js b/lib/wallet/client.js index c04d8707..224fc86c 100644 --- a/lib/wallet/client.js +++ b/lib/wallet/client.js @@ -21,24 +21,24 @@ class WalletClient extends NodeClient { async open() { await super.open(); - this.listen('block connect', (entry, txs) => { - this.emit('block connect', ...parseBlock(entry, txs)); + this.parse('block connect', (entry, txs) => { + return parseBlock(entry, txs); }); - this.listen('block disconnect', (entry) => { - this.emit('block disconnect', parseEntry(entry)); + this.parse('block disconnect', (entry) => { + return parseEntry(entry); }); - this.listen('block rescan', (entry, txs) => { - this.emit('block rescan', ...parseBlock(entry, txs)); + this.parse('block rescan', (entry, txs) => { + return parseBlock(entry, txs); }); - this.listen('chain reset', (tip) => { - this.emit('chain reset', parseEntry(tip)); + this.parse('chain reset', (tip) => { + return parseEntry(tip); }); - this.listen('tx', (tx) => { - this.emit('tx', TX.fromRaw(tx)); + this.parse('tx', (tx) => { + return TX.fromRaw(tx); }); } diff --git a/lib/wallet/nodeclient.js b/lib/wallet/nodeclient.js index db37fd70..61c237f2 100644 --- a/lib/wallet/nodeclient.js +++ b/lib/wallet/nodeclient.js @@ -24,7 +24,7 @@ function NodeClient(node) { this.node = node; this.network = node.network; this.filter = null; - this.listen = false; + this.listening = false; this._init(); } @@ -38,28 +38,28 @@ Object.setPrototypeOf(NodeClient.prototype, AsyncObject.prototype); NodeClient.prototype._init = function _init() { this.node.on('connect', (entry, block) => { - if (!this.listen) + if (!this.listening) return; this.emit('block connect', entry, block.txs); }); this.node.on('disconnect', (entry, block) => { - if (!this.listen) + if (!this.listening) return; this.emit('block disconnect', entry); }); this.node.on('tx', (tx) => { - if (!this.listen) + if (!this.listening) return; this.emit('tx', tx); }); this.node.on('reset', (tip) => { - if (!this.listen) + if (!this.listening) return; this.emit('chain reset', tip); @@ -72,7 +72,7 @@ NodeClient.prototype._init = function _init() { */ NodeClient.prototype._open = function _open(options) { - this.listen = true; + this.listening = true; this.emit('open'); return Promise.resolve(); }; @@ -83,7 +83,7 @@ NodeClient.prototype._open = function _open(options) { */ NodeClient.prototype._close = function _close() { - this.listen = false; + this.listening = false; this.emit('close'); return Promise.resolve(); }; @@ -193,7 +193,7 @@ NodeClient.prototype.getHashes = async function getHashes(start = -1, end = -1) NodeClient.prototype.rescan = function rescan(start) { return this.node.chain.scan(start, this.filter, (entry, txs) => { - return this.fire('block rescan', entry, txs); + return this.call('block rescan', entry, txs); }); }; diff --git a/lib/wallet/server.js b/lib/wallet/server.js index a998fa8a..68ed7a29 100644 --- a/lib/wallet/server.js +++ b/lib/wallet/server.js @@ -107,7 +107,7 @@ WalletNode.prototype._open = async function _open(callback) { await this.http.open(); - this.logger.info('Node is loaded.'); + this.logger.info('Wallet node is loaded.'); }; /** diff --git a/lib/wallet/walletdb.js b/lib/wallet/walletdb.js index 197dffc1..9975fb07 100644 --- a/lib/wallet/walletdb.js +++ b/lib/wallet/walletdb.js @@ -133,9 +133,11 @@ WalletDB.prototype._open = async function _open() { id: 'primary' }); + const addr = await wallet.receiveAddress(); + this.logger.info( 'Loaded primary wallet (id=%s, wid=%d, address=%s)', - wallet.id, wallet.wid, await wallet.receiveAddress()); + wallet.id, wallet.wid, addr.toString(this.network)); this.primary = wallet; }; @@ -215,7 +217,7 @@ WalletDB.prototype.bind = function bind() { } }); - this.client.on('block connect', async (entry, txs) => { + this.client.listen('block connect', async (entry, txs) => { try { await this.addBlock(entry, txs); } catch (e) { @@ -223,7 +225,7 @@ WalletDB.prototype.bind = function bind() { } }); - this.client.on('block disconnect', async (entry) => { + this.client.listen('block disconnect', async (entry) => { try { await this.removeBlock(entry); } catch (e) { @@ -231,7 +233,7 @@ WalletDB.prototype.bind = function bind() { } }); - this.client.on('block rescan', async (entry, txs) => { + this.client.hook('block rescan', async (entry, txs) => { try { await this.rescanBlock(entry, txs); } catch (e) { @@ -239,7 +241,7 @@ WalletDB.prototype.bind = function bind() { } }); - this.client.on('tx', async (tx) => { + this.client.listen('tx', async (tx) => { try { await this.addTX(tx); } catch (e) { @@ -247,7 +249,7 @@ WalletDB.prototype.bind = function bind() { } }); - this.client.on('chain reset', async (tip) => { + this.client.listen('chain reset', async (tip) => { try { await this.resetChain(tip); } catch (e) { @@ -1891,30 +1893,13 @@ WalletDB.prototype._removeBlock = async function _removeBlock(entry) { */ WalletDB.prototype.rescanBlock = async function rescanBlock(entry, txs) { - const unlock = await this.scanLock.lock(); - try { - return await this._rescanBlock(entry, txs); - } finally { - unlock(); - } -}; - -/** - * Rescan a block. - * @private - * @param {ChainEntry} entry - * @param {TX[]} txs - * @returns {Promise} - */ - -WalletDB.prototype._rescanBlock = async function _rescanBlock(entry, txs) { if (!this.rescanning) { - this.logger.warning('Unsolicited rescan block: %s.', entry.height); + this.logger.warning('Unsolicited rescan block: %d.', entry.height); return; } if (entry.height > this.state.height + 1) { - this.logger.warning('Unsolicited rescan block: %s.', entry.height); + this.logger.warning('Rescan block too high: %d.', entry.height); return; }