wallet: fixes for wallet server.

This commit is contained in:
Christopher Jeffrey 2017-10-26 15:35:23 -07:00
parent 877cecbedc
commit 749d4edc11
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
11 changed files with 108 additions and 70 deletions

10
bin/cli
View File

@ -430,7 +430,7 @@ CLI.prototype.retoken = async function retoken() {
CLI.prototype.rescan = async function rescan() { CLI.prototype.rescan = async function rescan() {
const height = this.config.uint(0); const height = this.config.uint(0);
await this.client.rescan(height); await this.wallet.rescan(height);
this.log('Rescanning...'); this.log('Rescanning...');
}; };
@ -658,6 +658,9 @@ CLI.prototype.handleWallet = async function handleWallet() {
case 'resend': case 'resend':
await this.resendWallet(); await this.resendWallet();
break; break;
case 'rescan':
await this.rescan();
break;
default: default:
this.log('Unrecognized command.'); this.log('Unrecognized command.');
this.log('Commands:'); this.log('Commands:');
@ -692,6 +695,7 @@ CLI.prototype.handleWallet = async function handleWallet() {
this.log(' $ lock: Lock wallet.'); this.log(' $ lock: Lock wallet.');
this.log(' $ unlock [passphrase] [timeout?]: Unlock wallet.'); this.log(' $ unlock [passphrase] [timeout?]: Unlock wallet.');
this.log(' $ resend: Resend pending transactions.'); this.log(' $ resend: Resend pending transactions.');
this.log(' $ rescan [height]: Rescan for transactions.');
this.log('Other Options:'); this.log('Other Options:');
this.log(' --passphrase [passphrase]: For signing & account creation.'); this.log(' --passphrase [passphrase]: For signing & account creation.');
this.log(' --account [account-name]: Account name.'); this.log(' --account [account-name]: Account name.');
@ -734,9 +738,6 @@ CLI.prototype.handleNode = async function handleNode() {
case 'block': case 'block':
await this.getBlock(); await this.getBlock();
break; break;
case 'rescan':
await this.rescan();
break;
case 'reset': case 'reset':
await this.reset(); await this.reset();
break; break;
@ -760,7 +761,6 @@ CLI.prototype.handleNode = async function handleNode() {
this.log(' $ tx [hash/address]: View transactions.'); this.log(' $ tx [hash/address]: View transactions.');
this.log(' $ coin [hash+index/address]: View coins.'); this.log(' $ coin [hash+index/address]: View coins.');
this.log(' $ block [hash/height]: View block.'); 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(' $ reset [height/hash]: Reset chain to desired block.');
this.log(' $ resend: Resend pending transactions.'); this.log(' $ resend: Resend pending transactions.');
this.log(' $ backup [path]: Backup the wallet db.'); this.log(' $ backup [path]: Backup the wallet db.');

View File

@ -905,7 +905,7 @@ Chain.prototype.reorganize = async function reorganize(competitor) {
competitor.height 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) { for (const entry of disconnect) {
const headers = entry.toHeaders(); const headers = entry.toHeaders();
const view = new CoinView(); const view = new CoinView();
await this.fire('disconnect', entry, headers, view); await this.call('disconnect', entry, headers, view);
} }
this.logger.warning( this.logger.warning(
@ -957,7 +957,7 @@ Chain.prototype.reorganizeSPV = async function reorganizeSPV(competitor) {
'Chain replay from height %d necessary.', 'Chain replay from height %d necessary.',
fork.height); 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); 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('tip', entry);
this.emit('reconnect', entry, block); 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('tip', entry);
this.emit('block', block, 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); this.emit('tip', tip);
if (!silent) if (!silent)
await this.fire('reset', tip); await this.call('reset', tip);
// Reset the orphan map completely. There may // Reset the orphan map completely. There may
// have been some orphans on a forked chain we // have been some orphans on a forked chain we

View File

@ -1307,7 +1307,7 @@ ChainDB.prototype.scan = async function scan(start, filter, iter) {
const block = await this.getBlock(entry.hash); const block = await this.getBlock(entry.hash);
const txs = []; const txs = [];
total++; total += 1;
if (!block) { if (!block) {
if (!this.options.spv && !this.options.prune) if (!this.options.spv && !this.options.prune)

View File

@ -498,7 +498,7 @@ class HTTP extends Server {
const tx = TX.fromRaw(data); const tx = TX.fromRaw(data);
this.node.send(tx); this.node.relay(tx);
return null; return null;
}); });
@ -655,7 +655,7 @@ class HTTP extends Server {
for (const tx of txs) for (const tx of txs)
raw.push(tx.toRaw()); raw.push(tx.toRaw());
return socket.fire('block rescan', block, raw); return socket.call('block rescan', block, raw);
}); });
return null; return null;
} }

View File

@ -209,6 +209,37 @@ AsyncEmitter.prototype.listenerCount = function listenerCount(type) {
return listeners.length; 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. * Emit an event synchronously.
* @method * @method
@ -280,7 +311,7 @@ AsyncEmitter.prototype.emit = function emit(type) {
* @returns {Promise} * @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.'); assert(typeof type === 'string', '`type` must be a string.');
const listeners = this._events[type]; const listeners = this._events[type];
@ -343,15 +374,15 @@ AsyncEmitter.prototype.fire = async function fire(type) {
* @returns {Promise} * @returns {Promise}
*/ */
AsyncEmitter.prototype.tryFire = async function tryFire(type) { AsyncEmitter.prototype.tryCall = async function tryCall(type) {
try { try {
await this.fire.apply(this, arguments); await this.call.apply(this, arguments);
} catch (e) { } catch (e) {
if (type === 'error') if (type === 'error')
return; return;
try { try {
await this.fire('error', e); await this.call('error', e);
} catch (e) { } catch (e) {
; ;
} }

View File

@ -61,7 +61,7 @@ AsyncObject.prototype.__open = async function __open() {
if (this.loaded) if (this.loaded)
return; return;
await this.fire('preopen'); await this.call('preopen');
this.loading = true; this.loading = true;
@ -76,7 +76,7 @@ AsyncObject.prototype.__open = async function __open() {
this.loading = false; this.loading = false;
this.loaded = true; this.loaded = true;
await this.fire('open'); await this.call('open');
}; };
/** /**
@ -105,7 +105,7 @@ AsyncObject.prototype.__close = async function __close() {
if (!this.loaded) if (!this.loaded)
return; return;
await this.fire('preclose'); await this.call('preclose');
this.closing = true; this.closing = true;
@ -120,7 +120,7 @@ AsyncObject.prototype.__close = async function __close() {
this.closing = false; this.closing = false;
this.loaded = 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.'); 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. * Add a hook listener.
* @param {String} type * @param {String} type
@ -174,8 +195,8 @@ AsyncObject.prototype.hook = function hook(type, handler) {
* @returns {Promise} * @returns {Promise}
*/ */
AsyncObject.prototype.fire = async function fire() { AsyncObject.prototype.call = async function call() {
await this.fireHook.apply(this, arguments); await this.callHook.apply(this, arguments);
this.emit.apply(this, arguments); this.emit.apply(this, arguments);
}; };
@ -188,7 +209,7 @@ AsyncObject.prototype.fire = async function fire() {
* @returns {Promise} * @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.'); assert(typeof type === 'string', '`type` must be a string.');
const listeners = this._hooks[type]; const listeners = this._hooks[type];

View File

@ -335,6 +335,7 @@ Bloom.prototype.toRaw = function toRaw() {
Bloom.prototype.fromReader = function fromReader(br) { Bloom.prototype.fromReader = function fromReader(br) {
this.filter = br.readVarBytes(); this.filter = br.readVarBytes();
this.size = this.filter.length * 8;
this.n = br.readU32(); this.n = br.readU32();
this.tweak = br.readU32(); this.tweak = br.readU32();
this.update = br.readU8(); this.update = br.readU8();

View File

@ -21,24 +21,24 @@ class WalletClient extends NodeClient {
async open() { async open() {
await super.open(); await super.open();
this.listen('block connect', (entry, txs) => { this.parse('block connect', (entry, txs) => {
this.emit('block connect', ...parseBlock(entry, txs)); return parseBlock(entry, txs);
}); });
this.listen('block disconnect', (entry) => { this.parse('block disconnect', (entry) => {
this.emit('block disconnect', parseEntry(entry)); return parseEntry(entry);
}); });
this.listen('block rescan', (entry, txs) => { this.parse('block rescan', (entry, txs) => {
this.emit('block rescan', ...parseBlock(entry, txs)); return parseBlock(entry, txs);
}); });
this.listen('chain reset', (tip) => { this.parse('chain reset', (tip) => {
this.emit('chain reset', parseEntry(tip)); return parseEntry(tip);
}); });
this.listen('tx', (tx) => { this.parse('tx', (tx) => {
this.emit('tx', TX.fromRaw(tx)); return TX.fromRaw(tx);
}); });
} }

View File

@ -24,7 +24,7 @@ function NodeClient(node) {
this.node = node; this.node = node;
this.network = node.network; this.network = node.network;
this.filter = null; this.filter = null;
this.listen = false; this.listening = false;
this._init(); this._init();
} }
@ -38,28 +38,28 @@ Object.setPrototypeOf(NodeClient.prototype, AsyncObject.prototype);
NodeClient.prototype._init = function _init() { NodeClient.prototype._init = function _init() {
this.node.on('connect', (entry, block) => { this.node.on('connect', (entry, block) => {
if (!this.listen) if (!this.listening)
return; return;
this.emit('block connect', entry, block.txs); this.emit('block connect', entry, block.txs);
}); });
this.node.on('disconnect', (entry, block) => { this.node.on('disconnect', (entry, block) => {
if (!this.listen) if (!this.listening)
return; return;
this.emit('block disconnect', entry); this.emit('block disconnect', entry);
}); });
this.node.on('tx', (tx) => { this.node.on('tx', (tx) => {
if (!this.listen) if (!this.listening)
return; return;
this.emit('tx', tx); this.emit('tx', tx);
}); });
this.node.on('reset', (tip) => { this.node.on('reset', (tip) => {
if (!this.listen) if (!this.listening)
return; return;
this.emit('chain reset', tip); this.emit('chain reset', tip);
@ -72,7 +72,7 @@ NodeClient.prototype._init = function _init() {
*/ */
NodeClient.prototype._open = function _open(options) { NodeClient.prototype._open = function _open(options) {
this.listen = true; this.listening = true;
this.emit('open'); this.emit('open');
return Promise.resolve(); return Promise.resolve();
}; };
@ -83,7 +83,7 @@ NodeClient.prototype._open = function _open(options) {
*/ */
NodeClient.prototype._close = function _close() { NodeClient.prototype._close = function _close() {
this.listen = false; this.listening = false;
this.emit('close'); this.emit('close');
return Promise.resolve(); return Promise.resolve();
}; };
@ -193,7 +193,7 @@ NodeClient.prototype.getHashes = async function getHashes(start = -1, end = -1)
NodeClient.prototype.rescan = function rescan(start) { NodeClient.prototype.rescan = function rescan(start) {
return this.node.chain.scan(start, this.filter, (entry, txs) => { return this.node.chain.scan(start, this.filter, (entry, txs) => {
return this.fire('block rescan', entry, txs); return this.call('block rescan', entry, txs);
}); });
}; };

View File

@ -107,7 +107,7 @@ WalletNode.prototype._open = async function _open(callback) {
await this.http.open(); await this.http.open();
this.logger.info('Node is loaded.'); this.logger.info('Wallet node is loaded.');
}; };
/** /**

View File

@ -133,9 +133,11 @@ WalletDB.prototype._open = async function _open() {
id: 'primary' id: 'primary'
}); });
const addr = await wallet.receiveAddress();
this.logger.info( this.logger.info(
'Loaded primary wallet (id=%s, wid=%d, address=%s)', '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; 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 { try {
await this.addBlock(entry, txs); await this.addBlock(entry, txs);
} catch (e) { } 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 { try {
await this.removeBlock(entry); await this.removeBlock(entry);
} catch (e) { } 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 { try {
await this.rescanBlock(entry, txs); await this.rescanBlock(entry, txs);
} catch (e) { } catch (e) {
@ -239,7 +241,7 @@ WalletDB.prototype.bind = function bind() {
} }
}); });
this.client.on('tx', async (tx) => { this.client.listen('tx', async (tx) => {
try { try {
await this.addTX(tx); await this.addTX(tx);
} catch (e) { } 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 { try {
await this.resetChain(tip); await this.resetChain(tip);
} catch (e) { } catch (e) {
@ -1891,30 +1893,13 @@ WalletDB.prototype._removeBlock = async function _removeBlock(entry) {
*/ */
WalletDB.prototype.rescanBlock = async function rescanBlock(entry, txs) { 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) { if (!this.rescanning) {
this.logger.warning('Unsolicited rescan block: %s.', entry.height); this.logger.warning('Unsolicited rescan block: %d.', entry.height);
return; return;
} }
if (entry.height > this.state.height + 1) { 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; return;
} }