refactor: locks.

This commit is contained in:
Christopher Jeffrey 2016-09-22 05:24:18 -07:00
parent 8cb6faa078
commit 4e11bbbf9f
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
7 changed files with 479 additions and 561 deletions

View File

@ -235,16 +235,6 @@ Chain.prototype._close = function close() {
return this.db.close(); return this.db.close();
}; };
/**
* Invoke mutex lock.
* @private
* @returns {Function} unlock
*/
Chain.prototype._lock = function _lock(block, force) {
return this.locker.lock(block, force);
};
/** /**
* Perform all necessary contextual verification on a block. * Perform all necessary contextual verification on a block.
* @private * @private
@ -929,23 +919,23 @@ Chain.prototype.setBestChain = co(function* setBestChain(entry, block, prev) {
* @param {Function} callback * @param {Function} callback
*/ */
Chain.prototype.reset = co(function* reset(height, force) { Chain.prototype.reset = co(function* reset(height) {
var unlock = yield this._lock(null, force); var unlock = yield this.locker.lock();
var result;
try { try {
result = yield this.db.reset(height); return yield this._reset(height);
} catch (e) { } finally {
unlock(); unlock();
throw e;
} }
});
Chain.prototype._reset = co(function* reset(height) {
var result = yield this.db.reset(height);
// 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
// no longer need. // no longer need.
this.purgeOrphans(); this.purgeOrphans();
unlock();
return result; return result;
}); });
@ -958,19 +948,23 @@ Chain.prototype.reset = co(function* reset(height, force) {
*/ */
Chain.prototype.resetTime = co(function* resetTime(ts) { Chain.prototype.resetTime = co(function* resetTime(ts) {
var unlock = yield this._lock(); var unlock = yield this.locker.lock();
var entry = yield this.byTime(ts);
if (!entry)
return unlock();
try { try {
yield this.reset(entry.height, true); return yield this._resetTime(ts);
} finally { } finally {
unlock(); unlock();
} }
}); });
Chain.prototype._resetTime = co(function* resetTime(ts) {
var entry = yield this.byTime(ts);
if (!entry)
return;
yield this._reset(entry.height);
});
/** /**
* Wait for the chain to drain (finish processing * Wait for the chain to drain (finish processing
* all of the blocks in its queue). * all of the blocks in its queue).
@ -999,14 +993,23 @@ Chain.prototype.isBusy = function isBusy() {
*/ */
Chain.prototype.add = co(function* add(block) { Chain.prototype.add = co(function* add(block) {
var ret, unlock, initial, hash, prevBlock; var unlock = yield this.locker.lock(block);
this.currentBlock = block.hash('hex');
try {
return yield this._add(block);
} finally {
this.currentBlock = null;
unlock();
}
});
Chain.prototype._add = co(function* add(block) {
var ret, initial, hash, prevBlock;
var height, checkpoint, orphan, entry; var height, checkpoint, orphan, entry;
var existing, prev; var existing, prev;
assert(this.loaded); assert(this.loaded);
unlock = yield this._lock(block);
ret = new VerifyResult(); ret = new VerifyResult();
initial = true; initial = true;
@ -1014,23 +1017,18 @@ Chain.prototype.add = co(function* add(block) {
hash = block.hash('hex'); hash = block.hash('hex');
prevBlock = block.prevBlock; prevBlock = block.prevBlock;
this.currentBlock = hash;
this._mark(); this._mark();
// Do not revalidate known invalid blocks. // Do not revalidate known invalid blocks.
if (this.invalid[hash] || this.invalid[prevBlock]) { if (this.invalid[hash] || this.invalid[prevBlock]) {
this.emit('invalid', block, block.getCoinbaseHeight()); this.emit('invalid', block, block.getCoinbaseHeight());
this.invalid[hash] = true; this.invalid[hash] = true;
this.currentBlock = null;
unlock();
throw new VerifyError(block, 'duplicate', 'duplicate', 100); throw new VerifyError(block, 'duplicate', 'duplicate', 100);
} }
// Do we already have this block? // Do we already have this block?
if (this.hasPending(hash)) { if (this.hasPending(hash)) {
this.emit('exists', block, block.getCoinbaseHeight()); this.emit('exists', block, block.getCoinbaseHeight());
this.currentBlock = null;
unlock();
throw new VerifyError(block, 'duplicate', 'duplicate', 0); throw new VerifyError(block, 'duplicate', 'duplicate', 0);
} }
@ -1047,8 +1045,6 @@ Chain.prototype.add = co(function* add(block) {
this.emit('orphan', block, block.getCoinbaseHeight()); this.emit('orphan', block, block.getCoinbaseHeight());
this.currentBlock = null;
unlock();
throw new VerifyError(block, 'invalid', 'bad-prevblk', 0); throw new VerifyError(block, 'invalid', 'bad-prevblk', 0);
} }
@ -1063,8 +1059,6 @@ Chain.prototype.add = co(function* add(block) {
if (initial && !block.verify(ret)) { if (initial && !block.verify(ret)) {
this.invalid[hash] = true; this.invalid[hash] = true;
this.emit('invalid', block, block.getCoinbaseHeight()); this.emit('invalid', block, block.getCoinbaseHeight());
this.currentBlock = null;
unlock();
throw new VerifyError(block, 'invalid', ret.reason, ret.score); throw new VerifyError(block, 'invalid', ret.reason, ret.score);
} }
@ -1073,8 +1067,6 @@ Chain.prototype.add = co(function* add(block) {
// Do we already have this block? // Do we already have this block?
if (existing) { if (existing) {
this.emit('exists', block, block.getCoinbaseHeight()); this.emit('exists', block, block.getCoinbaseHeight());
this.currentBlock = null;
unlock();
throw new VerifyError(block, 'duplicate', 'duplicate', 0); throw new VerifyError(block, 'duplicate', 'duplicate', 0);
} }
@ -1107,8 +1099,6 @@ Chain.prototype.add = co(function* add(block) {
this.emit('orphan', block, block.getCoinbaseHeight()); this.emit('orphan', block, block.getCoinbaseHeight());
this.currentBlock = null;
unlock();
throw new VerifyError(block, 'invalid', 'bad-prevblk', 0); throw new VerifyError(block, 'invalid', 'bad-prevblk', 0);
} }
@ -1122,8 +1112,6 @@ Chain.prototype.add = co(function* add(block) {
this.emit('fork', block, height, checkpoint); this.emit('fork', block, height, checkpoint);
this.currentBlock = null;
unlock();
throw new VerifyError(block, throw new VerifyError(block,
'checkpoint', 'checkpoint',
'checkpoint mismatch', 'checkpoint mismatch',
@ -1148,8 +1136,6 @@ Chain.prototype.add = co(function* add(block) {
block = block.toBlock(); block = block.toBlock();
} catch (e) { } catch (e) {
this.logger.error(e); this.logger.error(e);
this.currentBlock = null;
unlock();
throw new VerifyError(block, throw new VerifyError(block,
'malformed', 'malformed',
'error parsing message', 'error parsing message',
@ -1170,13 +1156,7 @@ Chain.prototype.add = co(function* add(block) {
// our tip's. Add the block but do _not_ // our tip's. Add the block but do _not_
// connect the inputs. // connect the inputs.
if (entry.chainwork.cmp(this.tip.chainwork) <= 0) { if (entry.chainwork.cmp(this.tip.chainwork) <= 0) {
try { yield this.db.save(entry, block, null, false);
yield this.db.save(entry, block, null, false);
} catch (e) {
this.currentBlock = null;
unlock();
throw e;
}
this.emit('competitor', block, entry); this.emit('competitor', block, entry);
@ -1184,13 +1164,7 @@ Chain.prototype.add = co(function* add(block) {
this.emit('competitor resolved', block, entry); this.emit('competitor resolved', block, entry);
} else { } else {
// Attempt to add block to the chain index. // Attempt to add block to the chain index.
try { yield this.setBestChain(entry, block, prev);
yield this.setBestChain(entry, block, prev);
} catch (e) {
this.currentBlock = null;
unlock();
throw e;
}
// Emit our block (and potentially resolved // Emit our block (and potentially resolved
// orphan) only if it is on the main chain. // orphan) only if it is on the main chain.
@ -1228,10 +1202,6 @@ Chain.prototype.add = co(function* add(block) {
this.synced = true; this.synced = true;
this.emit('full'); this.emit('full');
} }
this.currentBlock = null;
unlock();
}); });
/** /**
@ -1522,7 +1492,15 @@ Chain.prototype.getProgress = function getProgress() {
*/ */
Chain.prototype.getLocator = co(function* getLocator(start) { Chain.prototype.getLocator = co(function* getLocator(start) {
var unlock = yield this._lock(); var unlock = yield this.locker.lock();
try {
return yield this._getLocator(start);
} finally {
unlock();
}
});
Chain.prototype._getLocator = co(function* getLocator(start) {
var hashes = []; var hashes = [];
var step = 1; var step = 1;
var height, entry, main, hash; var height, entry, main, hash;
@ -1578,7 +1556,6 @@ Chain.prototype.getLocator = co(function* getLocator(start) {
hash = entry.hash; hash = entry.hash;
} }
unlock();
return hashes; return hashes;
}); });

View File

@ -85,6 +85,7 @@ function Mempool(options) {
this.orphans = {}; this.orphans = {};
this.tx = {}; this.tx = {};
this.spents = {}; this.spents = {};
this.currentTX = null;
this.coinIndex = new AddressIndex(this); this.coinIndex = new AddressIndex(this);
this.txIndex = new AddressIndex(this); this.txIndex = new AddressIndex(this);
@ -136,16 +137,6 @@ Mempool.prototype._close = function close() {
return Promise.resolve(null); return Promise.resolve(null);
}; };
/**
* Invoke mutex lock.
* @private
* @returns {Function} unlock
*/
Mempool.prototype._lock = function _lock(tx, force) {
return this.locker.lock(tx, force);
};
/** /**
* Notify the mempool that a new block has come * Notify the mempool that a new block has come
* in (removes all transactions contained in the * in (removes all transactions contained in the
@ -155,7 +146,15 @@ Mempool.prototype._lock = function _lock(tx, force) {
*/ */
Mempool.prototype.addBlock = co(function* addBlock(block) { Mempool.prototype.addBlock = co(function* addBlock(block) {
var unlock = yield this._lock(); var unlock = yield this.locker.lock();
try {
return yield this._addBlock(block);
} finally {
unlock();
}
});
Mempool.prototype._addBlock = co(function* addBlock(block) {
var entries = []; var entries = [];
var i, entry, tx, hash; var i, entry, tx, hash;
@ -190,7 +189,6 @@ Mempool.prototype.addBlock = co(function* addBlock(block) {
this.rejects.reset(); this.rejects.reset();
yield spawn.wait(); yield spawn.wait();
unlock();
}); });
/** /**
@ -202,6 +200,14 @@ Mempool.prototype.addBlock = co(function* addBlock(block) {
Mempool.prototype.removeBlock = co(function* removeBlock(block) { Mempool.prototype.removeBlock = co(function* removeBlock(block) {
var unlock = yield this.lock(); var unlock = yield this.lock();
try {
return yield this._removeBlock(block);
} finally {
unlock();
}
});
Mempool.prototype._removeBlock = co(function* removeBlock(block) {
var i, entry, tx, hash; var i, entry, tx, hash;
for (i = 0; i < block.txs.length; i++) { for (i = 0; i < block.txs.length; i++) {
@ -216,19 +222,12 @@ Mempool.prototype.removeBlock = co(function* removeBlock(block) {
entry = MempoolEntry.fromTX(tx, block.height); entry = MempoolEntry.fromTX(tx, block.height);
try { yield this._addUnchecked(entry);
yield this.addUnchecked(entry, true);
} catch (e) {
unlock();
throw e;
}
this.emit('unconfirmed', tx, block); this.emit('unconfirmed', tx, block);
} }
this.rejects.reset(); this.rejects.reset();
unlock();
}); });
/** /**
@ -503,6 +502,9 @@ Mempool.prototype.has = function has(hash) {
if (this.hasOrphan(hash)) if (this.hasOrphan(hash))
return true; return true;
if (hash === this.currentTX)
return true;
return this.hasTX(hash); return this.hasTX(hash);
}; };
@ -526,9 +528,11 @@ Mempool.prototype.hasReject = function hasReject(hash) {
*/ */
Mempool.prototype.addTX = co(function* addTX(tx) { Mempool.prototype.addTX = co(function* addTX(tx) {
var unlock = yield this._lock(tx); var unlock = yield this.locker.lock(tx);
var missing; var missing;
this.currentTX = tx.hash('hex');
try { try {
missing = yield this._addTX(tx); missing = yield this._addTX(tx);
} catch (err) { } catch (err) {
@ -536,10 +540,12 @@ Mempool.prototype.addTX = co(function* addTX(tx) {
if (!tx.hasWitness() && !err.malleated) if (!tx.hasWitness() && !err.malleated)
this.rejects.add(tx.hash()); this.rejects.add(tx.hash());
} }
this.currentTX = null;
unlock(); unlock();
throw err; throw err;
} }
this.currentTX = null;
unlock(); unlock();
return missing; return missing;
}); });
@ -651,7 +657,7 @@ Mempool.prototype._addTX = co(function* _addTX(tx) {
entry = MempoolEntry.fromTX(tx, this.chain.height); entry = MempoolEntry.fromTX(tx, this.chain.height);
yield this.verify(entry); yield this.verify(entry);
yield this.addUnchecked(entry, true); yield this._addUnchecked(entry);
if (this.limitMempoolSize(hash)) { if (this.limitMempoolSize(hash)) {
throw new VerifyError(tx, throw new VerifyError(tx,
@ -671,8 +677,16 @@ Mempool.prototype._addTX = co(function* _addTX(tx) {
* @param {Function} callback - Returns [{@link VerifyError}]. * @param {Function} callback - Returns [{@link VerifyError}].
*/ */
Mempool.prototype.addUnchecked = co(function* addUnchecked(entry, force) { Mempool.prototype.addUnchecked = co(function* addUnchecked(entry) {
var unlock = yield this._lock(null, force); var unlock = yield this.locker.lock();
try {
return yield this._addUnchecked(entry);
} finally {
unlock();
}
});
Mempool.prototype._addUnchecked = co(function* addUnchecked(entry) {
var i, resolved, tx, orphan; var i, resolved, tx, orphan;
this.trackEntry(entry); this.trackEntry(entry);
@ -709,7 +723,7 @@ Mempool.prototype.addUnchecked = co(function* addUnchecked(entry, force) {
} }
try { try {
yield this.addUnchecked(orphan, true); yield this._addUnchecked(orphan);
} catch (err) { } catch (err) {
this.emit('error', err); this.emit('error', err);
continue; continue;
@ -717,8 +731,6 @@ Mempool.prototype.addUnchecked = co(function* addUnchecked(entry, force) {
this.logger.spam('Resolved orphan %s in mempool.', orphan.tx.rhash); this.logger.spam('Resolved orphan %s in mempool.', orphan.tx.rhash);
} }
unlock();
}); });
/** /**

View File

@ -236,15 +236,6 @@ Peer.prototype._init = function init() {
} }
}; };
/**
* Invoke mutex lock.
* @private
*/
Peer.prototype._lock = function _lock(func, args, force) {
return this.locker.lock(func, args, force);
};
/** /**
* Handle `connect` event (called immediately * Handle `connect` event (called immediately
* if a socket was passed into peer). * if a socket was passed into peer).
@ -1120,20 +1111,28 @@ Peer.prototype._handleUTXOs = function _handleUTXOs(utxos) {
*/ */
Peer.prototype._handleGetUTXOs = co(function* _handleGetUTXOs(packet) { Peer.prototype._handleGetUTXOs = co(function* _handleGetUTXOs(packet) {
var unlock = yield this._lock(); var unlock = yield this.locker.lock();
try {
return yield this.__handleGetUTXOs(packet);
} finally {
unlock();
}
});
Peer.prototype.__handleGetUTXOs = co(function* _handleGetUTXOs(packet) {
var i, utxos, prevout, hash, index, coin; var i, utxos, prevout, hash, index, coin;
if (!this.chain.synced) if (!this.chain.synced)
return unlock(); return;
if (this.options.selfish) if (this.options.selfish)
return unlock(); return;
if (this.chain.db.options.spv) if (this.chain.db.options.spv)
return unlock(); return;
if (packet.prevout.length > 15) if (packet.prevout.length > 15)
return unlock(); return;
utxos = new packets.GetUTXOsPacket(); utxos = new packets.GetUTXOsPacket();
@ -1182,7 +1181,6 @@ Peer.prototype._handleGetUTXOs = co(function* _handleGetUTXOs(packet) {
utxos.tip = this.chain.tip.hash; utxos.tip = this.chain.tip.hash;
this.send(utxos); this.send(utxos);
unlock();
}); });
/** /**
@ -1203,21 +1201,29 @@ Peer.prototype._handleHaveWitness = function _handleHaveWitness(packet) {
*/ */
Peer.prototype._handleGetHeaders = co(function* _handleGetHeaders(packet) { Peer.prototype._handleGetHeaders = co(function* _handleGetHeaders(packet) {
var unlock = yield this._lock(); var unlock = yield this.locker.lock();
try {
return this.__handleGetHeaders(packet);
} finally {
unlock();
}
});
Peer.prototype.__handleGetHeaders = co(function* _handleGetHeaders(packet) {
var headers = []; var headers = [];
var hash, entry; var hash, entry;
if (!this.chain.synced) if (!this.chain.synced)
return unlock(); return;
if (this.options.selfish) if (this.options.selfish)
return unlock(); return;
if (this.chain.db.options.spv) if (this.chain.db.options.spv)
return unlock(); return;
if (this.chain.db.options.prune) if (this.chain.db.options.prune)
return unlock(); return;
if (packet.locator.length > 0) { if (packet.locator.length > 0) {
hash = yield this.chain.findLocator(packet.locator); hash = yield this.chain.findLocator(packet.locator);
@ -1243,8 +1249,6 @@ Peer.prototype._handleGetHeaders = co(function* _handleGetHeaders(packet) {
} }
this.sendHeaders(headers); this.sendHeaders(headers);
unlock();
}); });
/** /**
@ -1254,21 +1258,29 @@ Peer.prototype._handleGetHeaders = co(function* _handleGetHeaders(packet) {
*/ */
Peer.prototype._handleGetBlocks = co(function* _handleGetBlocks(packet) { Peer.prototype._handleGetBlocks = co(function* _handleGetBlocks(packet) {
var unlock = yield this._lock(); var unlock = yield this.locker.lock();
try {
return this.__handleGetBlocks(packet);
} finally {
unlock();
}
});
Peer.prototype.__handleGetBlocks = co(function* _handleGetBlocks(packet) {
var blocks = []; var blocks = [];
var hash; var hash;
if (!this.chain.synced) if (!this.chain.synced)
return unlock(); return;
if (this.options.selfish) if (this.options.selfish)
return unlock(); return;
if (this.chain.db.options.spv) if (this.chain.db.options.spv)
return unlock(); return;
if (this.chain.db.options.prune) if (this.chain.db.options.prune)
return unlock(); return;
hash = yield this.chain.findLocator(packet.locator); hash = yield this.chain.findLocator(packet.locator);
@ -1290,7 +1302,6 @@ Peer.prototype._handleGetBlocks = co(function* _handleGetBlocks(packet) {
} }
this.sendInv(blocks); this.sendInv(blocks);
unlock();
}); });
/** /**
@ -1476,7 +1487,15 @@ Peer.prototype._getItem = co(function* _getItem(item) {
*/ */
Peer.prototype._handleGetData = co(function* _handleGetData(packet) { Peer.prototype._handleGetData = co(function* _handleGetData(packet) {
var unlock = yield this._lock(); var unlock = yield this.locker.lock();
try {
return yield this.__handleGetData(packet);
} finally {
unlock();
}
});
Peer.prototype._handleGetData = co(function* _handleGetData(packet) {
var notFound = []; var notFound = [];
var items = packet.items; var items = packet.items;
var i, j, item, entry, tx, block; var i, j, item, entry, tx, block;
@ -1580,8 +1599,6 @@ Peer.prototype._handleGetData = co(function* _handleGetData(packet) {
if (notFound.length > 0) if (notFound.length > 0)
this.send(new packets.NotFoundPacket(notFound)); this.send(new packets.NotFoundPacket(notFound));
unlock();
}); });
/** /**

View File

@ -276,15 +276,6 @@ Pool.prototype._init = function _init() {
}); });
}; };
/**
* Invoke mutex lock.
* @private
*/
Pool.prototype._lock = function _lock(force) {
return this.locker.lock(force);
};
/** /**
* Open the pool, wait for the chain to load. * Open the pool, wait for the chain to load.
* @alias Pool#open * @alias Pool#open
@ -722,13 +713,20 @@ Pool.prototype.stopSync = function stopSync() {
*/ */
Pool.prototype._handleHeaders = co(function* _handleHeaders(headers, peer) { Pool.prototype._handleHeaders = co(function* _handleHeaders(headers, peer) {
var i, unlock, ret, header, hash, last; var unlock = yield this.locker.lock();
try {
return yield this.__handleHeaders(headers, peer);
} finally {
unlock();
}
});
Pool.prototype.__handleHeaders = co(function* _handleHeaders(headers, peer) {
var i, ret, header, hash, last;
if (!this.options.headers) if (!this.options.headers)
return; return;
unlock = yield this._lock();
ret = new VerifyResult(); ret = new VerifyResult();
this.logger.debug( this.logger.debug(
@ -751,13 +749,11 @@ Pool.prototype._handleHeaders = co(function* _handleHeaders(headers, peer) {
if (last && header.prevBlock !== last) { if (last && header.prevBlock !== last) {
peer.setMisbehavior(100); peer.setMisbehavior(100);
unlock();
throw new Error('Bad header chain.'); throw new Error('Bad header chain.');
} }
if (!header.verify(ret)) { if (!header.verify(ret)) {
peer.reject(header, 'invalid', ret.reason, 100); peer.reject(header, 'invalid', ret.reason, 100);
unlock();
throw new Error('Invalid header.'); throw new Error('Invalid header.');
} }
@ -778,8 +774,6 @@ Pool.prototype._handleHeaders = co(function* _handleHeaders(headers, peer) {
// the peer's chain. // the peer's chain.
if (last && headers.length === 2000) if (last && headers.length === 2000)
yield peer.getHeaders(last, null); yield peer.getHeaders(last, null);
unlock();
}); });
/** /**
@ -859,7 +853,15 @@ Pool.prototype._handleBlocks = co(function* _handleBlocks(hashes, peer) {
*/ */
Pool.prototype._handleInv = co(function* _handleInv(hashes, peer) { Pool.prototype._handleInv = co(function* _handleInv(hashes, peer) {
var unlock = yield this._lock(); var unlock = yield this.locker.lock();
try {
return yield this.__handleInv(hashes, peer);
} finally {
unlock();
}
});
Pool.prototype.__handleInv = co(function* _handleInv(hashes, peer) {
var i, hash; var i, hash;
// Ignore for now if we're still syncing // Ignore for now if we're still syncing
@ -868,7 +870,6 @@ Pool.prototype._handleInv = co(function* _handleInv(hashes, peer) {
if (!this.options.headers) { if (!this.options.headers) {
yield this._handleBlocks(hashes, peer); yield this._handleBlocks(hashes, peer);
unlock();
return; return;
} }
@ -878,7 +879,6 @@ Pool.prototype._handleInv = co(function* _handleInv(hashes, peer) {
} }
this.scheduleRequests(peer); this.scheduleRequests(peer);
unlock();
}); });
/** /**
@ -1048,7 +1048,7 @@ Pool.prototype.createPeer = function createPeer(addr, socket) {
self.addLoader(); self.addLoader();
}); });
peer.on('merkleblock', function(block) { peer.on('merkleblock', co(function *(block) {
if (!self.options.spv) if (!self.options.spv)
return; return;
@ -1057,17 +1057,20 @@ Pool.prototype.createPeer = function createPeer(addr, socket) {
// If the peer sent us a block that was added // If the peer sent us a block that was added
// to the chain (not orphans), reset the timeout. // to the chain (not orphans), reset the timeout.
self._handleBlock(block, peer).then(function() { try {
if (peer.isLoader()) { yield self._handleBlock(block, peer);
self.startInterval(); } catch (e) {
self.startTimeout();
}
}, function(err) {
self.emit('error', err); self.emit('error', err);
}); return;
}); }
peer.on('block', function(block) { if (peer.isLoader()) {
self.startInterval();
self.startTimeout();
}
}));
peer.on('block', co(function *(block) {
if (self.options.spv) if (self.options.spv)
return; return;
@ -1076,15 +1079,18 @@ Pool.prototype.createPeer = function createPeer(addr, socket) {
// If the peer sent us a block that was added // If the peer sent us a block that was added
// to the chain (not orphans), reset the timeout. // to the chain (not orphans), reset the timeout.
self._handleBlock(block, peer).then(function() { try {
if (peer.isLoader()) { yield self._handleBlock(block, peer);
self.startInterval(); } catch (e) {
self.startTimeout(); self.emit('error', e);
} return;
}, function(err) { }
self.emit('error', err);
}); if (peer.isLoader()) {
}); self.startInterval();
self.startTimeout();
}
}));
peer.on('error', function(err) { peer.on('error', function(err) {
self.emit('error', err, peer); self.emit('error', err, peer);
@ -1620,21 +1626,18 @@ Pool.prototype.exists = function exists(type, hash) {
* @param {Peer} peer * @param {Peer} peer
*/ */
Pool.prototype.scheduleRequests = function scheduleRequests(peer) { Pool.prototype.scheduleRequests = co(function* scheduleRequests(peer) {
var self = this;
if (this.scheduled) if (this.scheduled)
return; return;
this.scheduled = true; this.scheduled = true;
this.chain.onDrain().then(function() { yield this.chain.onDrain();
utils.nextTick(function() { yield spawn.wait();
self.sendRequests(peer);
self.scheduled = false; this.sendRequests(peer);
}); this.scheduled = false;
}); });
};
/** /**
* Send scheduled requests in the request queues. * Send scheduled requests in the request queues.

View File

@ -253,16 +253,6 @@ TXDB.prototype.emit = function emit(event, tx, info) {
this.wallet.emit(event, tx, info); this.wallet.emit(event, tx, info);
}; };
/**
* Invoke the mutex lock.
* @private
* @returns {Function} unlock
*/
TXDB.prototype._lock = function _lock(force) {
return this.locker.lock(force);
};
/** /**
* Prefix a key. * Prefix a key.
* @param {Buffer} key * @param {Buffer} key
@ -594,7 +584,15 @@ TXDB.prototype._resolveOrphans = co(function* _resolveOrphans(tx, index) {
*/ */
TXDB.prototype.add = co(function* add(tx, info) { TXDB.prototype.add = co(function* add(tx, info) {
var unlock = yield this._lock(); var unlock = yield this.locker.lock();
try {
return yield this.addl(tx, info);
} finally {
unlock();
}
});
TXDB.prototype.addl = co(function* add(tx, info) {
var hash, path, account; var hash, path, account;
var i, result, input, output, coin; var i, result, input, output, coin;
var prevout, key, address, spender, orphans; var prevout, key, address, spender, orphans;
@ -605,18 +603,14 @@ TXDB.prototype.add = co(function* add(tx, info) {
result = yield this._confirm(tx, info); result = yield this._confirm(tx, info);
// Ignore if we already have this tx. // Ignore if we already have this tx.
if (result) { if (result)
unlock();
return true; return true;
}
// Verify and get coins. // Verify and get coins.
result = yield this._verify(tx, info); result = yield this._verify(tx, info);
if (!result) { if (!result)
unlock();
return false; return false;
}
hash = tx.hash('hex'); hash = tx.hash('hex');
@ -667,7 +661,6 @@ TXDB.prototype.add = co(function* add(tx, info) {
yield this._addOrphan(prevout, spender); yield this._addOrphan(prevout, spender);
} catch (e) { } catch (e) {
this.drop(); this.drop();
unlock();
throw e; throw e;
} }
continue; continue;
@ -697,7 +690,6 @@ TXDB.prototype.add = co(function* add(tx, info) {
orphans = yield this._resolveOrphans(tx, i); orphans = yield this._resolveOrphans(tx, i);
} catch (e) { } catch (e) {
this.drop(); this.drop();
unlock();
throw e; throw e;
} }
@ -714,12 +706,7 @@ TXDB.prototype.add = co(function* add(tx, info) {
this.coinCache.set(key, coin); this.coinCache.set(key, coin);
} }
try { yield this.commit();
yield this.commit();
} catch (e) {
unlock();
throw e;
}
// Clear any locked coins to free up memory. // Clear any locked coins to free up memory.
this.unlockTX(tx); this.unlockTX(tx);
@ -729,7 +716,6 @@ TXDB.prototype.add = co(function* add(tx, info) {
if (tx.ts !== 0) if (tx.ts !== 0)
this.emit('confirmed', tx, info); this.emit('confirmed', tx, info);
unlock();
return true; return true;
}); });
@ -964,11 +950,23 @@ TXDB.prototype._confirm = co(function* _confirm(tx, info) {
* @param {Function} callback - Returns [Error]. * @param {Function} callback - Returns [Error].
*/ */
TXDB.prototype.remove = co(function* remove(hash, force) { TXDB.prototype.remove = co(function* remove(hash) {
var unlock = yield this._lock(force); var unlock = yield this.locker.lock();
var info = yield this._removeRecursive(); try {
return yield this.removel(hash);
} finally {
unlock();
}
});
unlock(); TXDB.prototype.removel = co(function* remove(hash) {
var tx = yield this.getTX(hash);
var info;
if (!tx)
return;
info = yield this._removeRecursive(tx);
if (!info) if (!info)
return; return;
@ -1087,33 +1085,26 @@ TXDB.prototype._remove = co(function* remove(tx, info) {
* @param {Function} callback * @param {Function} callback
*/ */
TXDB.prototype.unconfirm = co(function* unconfirm(hash, force) { TXDB.prototype.unconfirm = co(function* unconfirm(hash) {
var unlock = yield this._lock(force); var unlock = yield this.locker.lock();
var tx, info, result;
try { try {
tx = yield this.getTX(hash); return yield this.unconfirml(hash);
} catch (e) { } finally {
unlock(); unlock();
throw e;
} }
});
if (!tx) { TXDB.prototype.unconfirml = co(function* unconfirm(hash) {
unlock(); var tx = yield this.getTX(hash);
var info, result;
if (!tx)
return false; return false;
}
try { info = yield this.getInfo(tx);
info = yield this.getInfo(tx);
} catch (e) {
unlock();
throw e;
}
if (!info) { if (!info)
unlock();
return false; return false;
}
this.start(); this.start();
@ -1121,18 +1112,11 @@ TXDB.prototype.unconfirm = co(function* unconfirm(hash, force) {
result = yield this._unconfirm(tx, info); result = yield this._unconfirm(tx, info);
} catch (e) { } catch (e) {
this.drop(); this.drop();
unlock();
throw e; throw e;
} }
try { yield this.commit();
yield this.commit();
} catch (e) {
unlock();
throw e;
}
unlock();
return result; return result;
}); });
@ -1943,7 +1927,15 @@ TXDB.prototype.getAccountBalance = co(function* getBalance(account) {
*/ */
TXDB.prototype.zap = co(function* zap(account, age) { TXDB.prototype.zap = co(function* zap(account, age) {
var unlock = yield this._lock(); var unlock = yield this.locker.lock();
try {
return yield this._zap(account, age);
} finally {
unlock();
}
});
TXDB.prototype._zap = co(function* zap(account, age) {
var i, txs, tx, hash; var i, txs, tx, hash;
if (!utils.isUInt32(age)) if (!utils.isUInt32(age))
@ -1961,15 +1953,8 @@ TXDB.prototype.zap = co(function* zap(account, age) {
if (tx.ts !== 0) if (tx.ts !== 0)
continue; continue;
try { yield this.removel(hash);
yield this.remove(hash, true);
} catch (e) {
unlock();
throw e;
}
} }
unlock();
}); });
/** /**

View File

@ -77,24 +77,6 @@ function Wallet(db, options) {
utils.inherits(Wallet, EventEmitter); utils.inherits(Wallet, EventEmitter);
/**
* Invoke write mutex lock.
* @private
*/
Wallet.prototype._lockWrite = function _lockWrite(force) {
return this.writeLock.lock(force);
};
/**
* Invoke funding mutex lock.
* @private
*/
Wallet.prototype._lockFund = function _lockFund(force) {
return this.fundLock.lock(force);
};
/** /**
* Inject properties from options object. * Inject properties from options object.
* @private * @private
@ -246,7 +228,15 @@ Wallet.prototype.destroy = function destroy() {
*/ */
Wallet.prototype.addKey = co(function* addKey(account, key) { Wallet.prototype.addKey = co(function* addKey(account, key) {
var unlock = yield this._lockWrite(); var unlock = yield this.writeLock.lock();
try {
return yield this._addKey(account, key);
} finally {
unlock();
}
});
Wallet.prototype._addKey = co(function* addKey(account, key) {
var result; var result;
if (!key) { if (!key) {
@ -259,10 +249,8 @@ Wallet.prototype.addKey = co(function* addKey(account, key) {
account = yield this.getAccount(account); account = yield this.getAccount(account);
if (!account) { if (!account)
unlock();
throw new Error('Account not found.'); throw new Error('Account not found.');
}
this.start(); this.start();
@ -270,13 +258,11 @@ Wallet.prototype.addKey = co(function* addKey(account, key) {
result = yield account.addKey(key, true); result = yield account.addKey(key, true);
} catch (e) { } catch (e) {
this.drop(); this.drop();
unlock();
throw e; throw e;
} }
yield this.commit(); yield this.commit();
unlock();
return result; return result;
}); });
@ -288,7 +274,15 @@ Wallet.prototype.addKey = co(function* addKey(account, key) {
*/ */
Wallet.prototype.removeKey = co(function* removeKey(account, key) { Wallet.prototype.removeKey = co(function* removeKey(account, key) {
var unlock = yield this._lockWrite(); var unlock = yield this.writeLock.lock();
try {
return yield this._removeKey(account, key);
} finally {
unlock();
}
});
Wallet.prototype._removeKey = co(function* removeKey(account, key) {
var result; var result;
if (!key) { if (!key) {
@ -301,10 +295,8 @@ Wallet.prototype.removeKey = co(function* removeKey(account, key) {
account = yield this.getAccount(account); account = yield this.getAccount(account);
if (!account) { if (!account)
unlock();
throw new Error('Account not found.'); throw new Error('Account not found.');
}
this.start(); this.start();
@ -312,13 +304,11 @@ Wallet.prototype.removeKey = co(function* removeKey(account, key) {
result = yield account.removeKey(key, true); result = yield account.removeKey(key, true);
} catch (e) { } catch (e) {
this.drop(); this.drop();
unlock();
throw e; throw e;
} }
yield this.commit(); yield this.commit();
unlock();
return result; return result;
}); });
@ -330,27 +320,30 @@ Wallet.prototype.removeKey = co(function* removeKey(account, key) {
*/ */
Wallet.prototype.setPassphrase = co(function* setPassphrase(old, new_) { Wallet.prototype.setPassphrase = co(function* setPassphrase(old, new_) {
var unlock = yield this._lockWrite(); var unlock = yield this.writeLock.lock();
try {
return yield this._setPassphrase(old, new_);
} finally {
unlock();
}
});
if (!new_) { Wallet.prototype._setPassphrase = co(function* setPassphrase(old, new_) {
if (new_ == null) {
new_ = old; new_ = old;
old = null; old = null;
} }
try { if (old)
if (old) yield this.master.decrypt(old);
yield this.master.decrypt(old);
if (new_) if (new_)
yield this.master.encrypt(new_); yield this.master.encrypt(new_);
} catch (e) {
unlock();
throw e;
}
this.start(); this.start();
this.save(); this.save();
yield this.commit(); yield this.commit();
unlock();
}); });
/** /**
@ -360,24 +353,25 @@ Wallet.prototype.setPassphrase = co(function* setPassphrase(old, new_) {
*/ */
Wallet.prototype.retoken = co(function* retoken(passphrase) { Wallet.prototype.retoken = co(function* retoken(passphrase) {
var unlock = yield this._lockWrite(); var unlock = yield this.writeLock.lock();
var master;
try { try {
master = yield this.unlock(passphrase); return yield this._retoken(passphrase);
} catch (e) { } finally {
unlock(); unlock();
throw e;
} }
});
Wallet.prototype._retoken = co(function* retoken(passphrase) {
var master = yield this.unlock(passphrase);
this.tokenDepth++; this.tokenDepth++;
this.token = this.getToken(master, this.tokenDepth); this.token = this.getToken(master, this.tokenDepth);
this.start(); this.start();
this.save(); this.save();
yield this.commit(); yield this.commit();
unlock();
return this.token; return this.token;
}); });
@ -461,7 +455,15 @@ Wallet.prototype.getToken = function getToken(master, nonce) {
*/ */
Wallet.prototype.createAccount = co(function* createAccount(options) { Wallet.prototype.createAccount = co(function* createAccount(options) {
var unlock = yield this._lockWrite(); var unlock = yield this.writeLock.lock();
try {
return yield this._createAccount(options);
} finally {
unlock();
}
});
Wallet.prototype._createAccount = co(function* createAccount(options) {
var passphrase = options.passphrase; var passphrase = options.passphrase;
var timeout = options.timeout; var timeout = options.timeout;
var name = options.name; var name = options.name;
@ -473,12 +475,7 @@ Wallet.prototype.createAccount = co(function* createAccount(options) {
if (!name) if (!name)
name = this.accountDepth + ''; name = this.accountDepth + '';
try { master = yield this.unlock(passphrase, timeout);
master = yield this.unlock(passphrase, timeout);
} catch (e) {
unlock();
throw e;
}
key = master.deriveAccount44(this.accountDepth); key = master.deriveAccount44(this.accountDepth);
@ -502,14 +499,13 @@ Wallet.prototype.createAccount = co(function* createAccount(options) {
account = yield this.db.createAccount(options); account = yield this.db.createAccount(options);
} catch (e) { } catch (e) {
this.drop(); this.drop();
unlock();
throw e; throw e;
} }
this.accountDepth++; this.accountDepth++;
this.save(); this.save();
yield this.commit(); yield this.commit();
unlock();
return account; return account;
}); });
@ -614,7 +610,15 @@ Wallet.prototype.createChange = function createChange(account) {
*/ */
Wallet.prototype.createAddress = co(function* createAddress(account, change) { Wallet.prototype.createAddress = co(function* createAddress(account, change) {
var unlock = yield this._lockWrite(); var unlock = yield this.writeLock.lock();
try {
return yield this._createAddress(account, change);
} finally {
unlock();
}
});
Wallet.prototype._createAddress = co(function* createAddress(account, change) {
var result; var result;
if (typeof account === 'boolean') { if (typeof account === 'boolean') {
@ -625,30 +629,21 @@ Wallet.prototype.createAddress = co(function* createAddress(account, change) {
if (account == null) if (account == null)
account = 0; account = 0;
try { account = yield this.getAccount(account);
account = yield this.getAccount(account);
} catch (e) {
unlock();
throw e;
}
if (!account) { if (!account)
unlock();
throw new Error('Account not found.'); throw new Error('Account not found.');
}
this.start(); this.start();
try { try {
result = yield account.createAddress(change, true); result = yield account.createAddress(change);
} catch (e) { } catch (e) {
this.drop(); this.drop();
unlock();
throw e; throw e;
} }
yield this.commit(); yield this.commit();
unlock();
return result; return result;
}); });
@ -760,7 +755,15 @@ Wallet.prototype.getPaths = co(function* getPaths(account) {
*/ */
Wallet.prototype.importKey = co(function* importKey(account, ring, passphrase) { Wallet.prototype.importKey = co(function* importKey(account, ring, passphrase) {
var unlock = yield this._lockWrite(); var unlock = yield this.writeLock.lock();
try {
return yield this._importKey(account, ring, passphrase);
} finally {
unlock();
}
});
Wallet.prototype._importKey = co(function* importKey(account, ring, passphrase) {
var exists, raw, path; var exists, raw, path;
if (account && typeof account === 'object') { if (account && typeof account === 'object') {
@ -772,36 +775,20 @@ Wallet.prototype.importKey = co(function* importKey(account, ring, passphrase) {
if (account == null) if (account == null)
account = 0; account = 0;
try { exists = yield this.getPath(ring.getHash('hex'));
exists = yield this.getPath(ring.getHash('hex'));
} catch (e) {
unlock();
throw e;
}
if (exists) { if (exists)
unlock();
throw new Error('Key already exists.'); throw new Error('Key already exists.');
}
account = yield this.getAccount(account); account = yield this.getAccount(account);
if (!account) { if (!account)
unlock();
throw new Error('Account not found.'); throw new Error('Account not found.');
}
if (account.type !== bcoin.account.types.PUBKEYHASH) { if (account.type !== bcoin.account.types.PUBKEYHASH)
unlock();
throw new Error('Cannot import into non-pkh account.'); throw new Error('Cannot import into non-pkh account.');
}
try { yield this.unlock(passphrase);
yield this.unlock(passphrase);
} catch (e) {
unlock();
throw e;
}
raw = ring.toRaw(); raw = ring.toRaw();
path = Path.fromAccount(account, ring); path = Path.fromAccount(account, ring);
@ -821,12 +808,10 @@ Wallet.prototype.importKey = co(function* importKey(account, ring, passphrase) {
yield account.saveAddress([ring], true); yield account.saveAddress([ring], true);
} catch (e) { } catch (e) {
this.drop(); this.drop();
unlock();
throw e; throw e;
} }
yield this.commit(); yield this.commit();
unlock();
}); });
/** /**
@ -854,31 +839,33 @@ Wallet.prototype.importKey = co(function* importKey(account, ring, passphrase) {
*/ */
Wallet.prototype.fund = co(function* fund(tx, options, force) { Wallet.prototype.fund = co(function* fund(tx, options, force) {
var unlock = yield this._lockFund(force); var unlock = yield this.fundLock.lock(force);
try {
return yield this._fund(tx, options);
} finally {
unlock();
}
});
Wallet.prototype._fund = co(function* fund(tx, options, force) {
var rate, account, coins; var rate, account, coins;
if (!options) if (!options)
options = {}; options = {};
if (!this.initialized) { if (!this.initialized)
unlock();
throw new Error('Wallet is not initialized.'); throw new Error('Wallet is not initialized.');
}
if (options.account != null) { if (options.account != null) {
account = yield this.getAccount(options.account); account = yield this.getAccount(options.account);
if (!account) { if (!account)
unlock();
throw new Error('Account not found.'); throw new Error('Account not found.');
}
} else { } else {
account = this.account; account = this.account;
} }
if (!account.initialized) { if (!account.initialized)
unlock();
throw new Error('Account is not initialized.'); throw new Error('Account is not initialized.');
}
coins = yield this.getCoins(options.account); coins = yield this.getCoins(options.account);
@ -894,26 +881,22 @@ Wallet.prototype.fund = co(function* fund(tx, options, force) {
// Don't use any locked coins. // Don't use any locked coins.
coins = this.tx.filterLocked(coins); coins = this.tx.filterLocked(coins);
try { tx.fund(coins, {
tx.fund(coins, { selection: options.selection,
selection: options.selection, round: options.round,
round: options.round, confirmations: options.confirmations,
confirmations: options.confirmations, free: options.free,
free: options.free, hardFee: options.hardFee,
hardFee: options.hardFee, subtractFee: options.subtractFee,
subtractFee: options.subtractFee, changeAddress: account.changeAddress.getAddress(),
changeAddress: account.changeAddress.getAddress(), height: this.db.height,
height: this.db.height, rate: rate,
rate: rate, maxFee: options.maxFee,
maxFee: options.maxFee, m: account.m,
m: account.m, n: account.n,
n: account.n, witness: account.witness,
witness: account.witness, script: account.receiveAddress.script
script: account.receiveAddress.script });
});
} finally {
unlock();
}
}); });
/** /**
@ -977,27 +960,21 @@ Wallet.prototype.createTX = co(function* createTX(options, force) {
*/ */
Wallet.prototype.send = co(function* send(options) { Wallet.prototype.send = co(function* send(options) {
var unlock = yield this._lockFund(); var unlock = yield this.fundLock.lock();
var tx;
try { try {
tx = yield this.createTX(options, true); return yield this._send(options);
} catch (e) { } finally {
unlock(); unlock();
throw e;
} }
});
try { Wallet.prototype._send = co(function* send(options) {
yield this.sign(tx); var tx = yield this.createTX(options, true);
} catch (e) {
unlock();
throw e;
}
if (!tx.isSigned()) { yield this.sign(tx);
unlock();
if (!tx.isSigned())
throw new Error('TX could not be fully signed.'); throw new Error('TX could not be fully signed.');
}
tx = tx.toTX(); tx = tx.toTX();
@ -1006,7 +983,6 @@ Wallet.prototype.send = co(function* send(options) {
this.logger.debug('Sending wallet tx (%s): %s', this.id, tx.rhash); this.logger.debug('Sending wallet tx (%s): %s', this.id, tx.rhash);
this.db.emit('send', tx); this.db.emit('send', tx);
unlock();
return tx; return tx;
}); });
@ -1160,7 +1136,15 @@ Wallet.prototype.getOutputPaths = co(function* getOutputPaths(tx) {
*/ */
Wallet.prototype.syncOutputDepth = co(function* syncOutputDepth(info) { Wallet.prototype.syncOutputDepth = co(function* syncOutputDepth(info) {
var unlock = yield this._lockWrite(); var unlock = yield this.writeLock.lock();
try {
return yield this._syncOutputDepth(info);
} finally {
unlock();
}
});
Wallet.prototype._syncOutputDepth = co(function* syncOutputDepth(info) {
var receive = []; var receive = [];
var accounts = {}; var accounts = {};
var i, j, path, paths, account; var i, j, path, paths, account;
@ -1204,22 +1188,12 @@ Wallet.prototype.syncOutputDepth = co(function* syncOutputDepth(info) {
receiveDepth += 2; receiveDepth += 2;
changeDepth += 2; changeDepth += 2;
try { account = yield this.getAccount(account);
account = yield this.getAccount(account);
} catch (e) {
unlock();
throw e;
}
if (!account) if (!account)
continue; continue;
try { ret = yield account.setDepth(receiveDepth, changeDepth);
ret = yield account.setDepth(receiveDepth, changeDepth);
} catch (e) {
unlock();
throw e;
}
rcv = ret[0]; rcv = ret[0];
chng = ret[1]; chng = ret[1];
@ -1235,7 +1209,6 @@ Wallet.prototype.syncOutputDepth = co(function* syncOutputDepth(info) {
yield this.commit(); yield this.commit();
unlock();
return receive; return receive;
}); });
@ -2002,15 +1975,6 @@ MasterKey.fromOptions = function fromOptions(options) {
return new MasterKey().fromOptions(options); return new MasterKey().fromOptions(options);
}; };
/**
* Invoke mutex lock.
* @private
*/
MasterKey.prototype._lock = function _lock(force) {
return this.locker.lock(force);
};
/** /**
* Decrypt the key and set a timeout to destroy decrypted data. * Decrypt the key and set a timeout to destroy decrypted data.
* @param {Buffer|String} passphrase - Zero this yourself. * @param {Buffer|String} passphrase - Zero this yourself.
@ -2019,28 +1983,27 @@ MasterKey.prototype._lock = function _lock(force) {
*/ */
MasterKey.prototype.unlock = co(function* _unlock(passphrase, timeout) { MasterKey.prototype.unlock = co(function* _unlock(passphrase, timeout) {
var unlock = yield this._lock(); var unlock = yield this.locker.lock();
try {
return yield this._unlock(passphrase, timeout);
} finally {
unlock();
}
});
MasterKey.prototype._unlock = co(function* _unlock(passphrase, timeout) {
var data, key; var data, key;
if (this.key) { if (this.key)
unlock();
return this.key; return this.key;
}
if (!passphrase) { if (!passphrase)
unlock();
throw new Error('No passphrase.'); throw new Error('No passphrase.');
}
assert(this.encrypted); assert(this.encrypted);
try { key = yield crypto.derive(passphrase);
key = yield crypto.derive(passphrase); data = crypto.decipher(this.ciphertext, key, this.iv);
data = crypto.decipher(this.ciphertext, key, this.iv);
} catch (e) {
unlock();
throw e;
}
this.key = bcoin.hd.fromExtended(data); this.key = bcoin.hd.fromExtended(data);
@ -2048,7 +2011,6 @@ MasterKey.prototype.unlock = co(function* _unlock(passphrase, timeout) {
this.aesKey = key; this.aesKey = key;
unlock();
return this.key; return this.key;
}); });
@ -2137,38 +2099,33 @@ MasterKey.prototype.destroy = function destroy() {
*/ */
MasterKey.prototype.decrypt = co(function* decrypt(passphrase) { MasterKey.prototype.decrypt = co(function* decrypt(passphrase) {
var unlock = yield this._lock(); var unlock = yield this.locker.lock();
try {
return yield this._decrypt(passphrase);
} finally {
unlock();
}
});
MasterKey.prototype._decrypt = co(function* decrypt(passphrase) {
var data; var data;
if (!this.encrypted) { if (!this.encrypted) {
assert(this.key); assert(this.key);
return unlock(); return;
} }
if (!passphrase) if (!passphrase)
return unlock(); return;
this.destroy(); this.destroy();
try { data = yield crypto.decrypt(this.ciphertext, passphrase, this.iv);
data = yield crypto.decrypt(this.ciphertext, passphrase, this.iv);
} catch (e) {
unlock();
throw e;
}
try {
this.key = bcoin.hd.fromExtended(data);
} catch (e) {
unlock();
throw e;
}
this.key = bcoin.hd.fromExtended(data);
this.encrypted = false; this.encrypted = false;
this.iv = null; this.iv = null;
this.ciphertext = null; this.ciphertext = null;
unlock();
}); });
/** /**
@ -2178,33 +2135,34 @@ MasterKey.prototype.decrypt = co(function* decrypt(passphrase) {
*/ */
MasterKey.prototype.encrypt = co(function* encrypt(passphrase) { MasterKey.prototype.encrypt = co(function* encrypt(passphrase) {
var unlock = yield this._lock(); var unlock = yield this.locker.lock();
try {
return yield this._encrypt(passphrase);
} finally {
unlock();
}
});
MasterKey.prototype._encrypt = co(function* encrypt(passphrase) {
var data, iv; var data, iv;
if (this.encrypted) if (this.encrypted)
return unlock(); return;
if (!passphrase) if (!passphrase)
return unlock(); return;
data = this.key.toExtended(); data = this.key.toExtended();
iv = crypto.randomBytes(16); iv = crypto.randomBytes(16);
this.stop(); this.stop();
try { data = yield crypto.encrypt(data, passphrase, iv);
data = yield crypto.encrypt(data, passphrase, iv);
} catch (e) {
unlock();
throw e;
}
this.key = null; this.key = null;
this.encrypted = true; this.encrypted = true;
this.iv = iv; this.iv = iv;
this.ciphertext = data; this.ciphertext = data;
unlock();
}); });
/** /**

View File

@ -193,33 +193,6 @@ WalletDB.prototype._init = function _init() {
} }
}; };
/**
* Invoke wallet read mutex lock.
* @private
*/
WalletDB.prototype._lockRead = function _lockRead(key, force) {
return this.readLock.lock(key, force);
};
/**
* Invoke wallet write mutex lock.
* @private
*/
WalletDB.prototype._lockWrite = function _lockWrite(key, force) {
return this.writeLock.lock(key, force);
};
/**
* Invoke tx handling mutex lock.
* @private
*/
WalletDB.prototype._lockTX = function _lockTX(force) {
return this.txLock.lock(force);
};
/** /**
* Open the walletdb, wait for the database to load. * Open the walletdb, wait for the database to load.
* @alias WalletDB#open * @alias WalletDB#open
@ -474,7 +447,6 @@ WalletDB.prototype.getWalletID = function getWalletID(id) {
*/ */
WalletDB.prototype.get = co(function* get(wid) { WalletDB.prototype.get = co(function* get(wid) {
var self = this;
var wallet, unlock; var wallet, unlock;
wid = yield this.getWalletID(wid); wid = yield this.getWalletID(wid);
@ -486,31 +458,30 @@ WalletDB.prototype.get = co(function* get(wid) {
return wallet; return wallet;
// NOTE: Lock must start here! // NOTE: Lock must start here!
unlock = yield this._lockRead(wid); unlock = yield this.readLock.lock(wid);
try { try {
wallet = yield this.db.fetch(layout.w(wid), function(data) { return yield this._get(wid);
return bcoin.wallet.fromRaw(self, data); } finally {
});
} catch (e) {
unlock(); unlock();
throw e;
} }
});
if (!wallet) { WalletDB.prototype._get = co(function* get(wid) {
unlock(); var self = this;
var wallet;
wallet = yield this.db.fetch(layout.w(wid), function(data) {
return bcoin.wallet.fromRaw(self, data);
});
if (!wallet)
return; return;
}
try { this.register(wallet);
this.register(wallet);
yield wallet.open(); yield wallet.open();
} catch (e) {
unlock();
throw e;
}
unlock();
return wallet; return wallet;
}); });
@ -561,33 +532,36 @@ WalletDB.prototype.auth = co(function* auth(wid, token) {
*/ */
WalletDB.prototype.create = co(function* create(options) { WalletDB.prototype.create = co(function* create(options) {
var unlock, wallet, exists; var unlock;
if (!options) if (!options)
options = {}; options = {};
unlock = yield this._lockWrite(options.id); unlock = yield this.writeLock.lock(options.id);
exists = yield this.has(options.id);
if (exists) {
unlock();
throw new Error('Wallet already exists.');
}
try { try {
wallet = bcoin.wallet.fromOptions(this, options); return yield this._create(options);
wallet.wid = this.depth++; } finally {
this.register(wallet);
yield wallet.init(options);
} catch (e) {
unlock(); unlock();
throw e;
} }
});
WalletDB.prototype._create = co(function* create(options) {
var exists = yield this.has(options.id);
var wallet;
if (exists)
throw new Error('Wallet already exists.');
wallet = bcoin.wallet.fromOptions(this, options);
wallet.wid = this.depth++;
this.register(wallet);
yield wallet.init(options);
this.logger.info('Created wallet %s.', wallet.id); this.logger.info('Created wallet %s.', wallet.id);
unlock();
return wallet; return wallet;
}); });
@ -963,8 +937,16 @@ WalletDB.prototype.getWallets = function getWallets() {
*/ */
WalletDB.prototype.rescan = co(function* rescan(chaindb, height) { WalletDB.prototype.rescan = co(function* rescan(chaindb, height) {
var unlock = yield this.txLock.lock();
try {
return yield this._rescan(chaindb, height);
} finally {
unlock();
}
});
WalletDB.prototype._rescan = co(function* rescan(chaindb, height) {
var self = this; var self = this;
var unlock = yield this._lockTX();
var hashes; var hashes;
if (height == null) if (height == null)
@ -974,16 +956,9 @@ WalletDB.prototype.rescan = co(function* rescan(chaindb, height) {
this.logger.info('Scanning for %d addresses.', hashes.length); this.logger.info('Scanning for %d addresses.', hashes.length);
try { yield chaindb.scan(height, hashes, co(function *(block, txs) {
yield chaindb.scan(height, hashes, co(function *(block, txs) { yield self._addBlock(block, txs);
yield self.addBlock(block, txs, true); }));
}));
} catch (e) {
unlock();
throw e;
}
unlock();
}); });
/** /**
@ -1235,19 +1210,21 @@ WalletDB.prototype.getWalletsByTX = function getWalletsByTX(hash) {
* @param {Function} callback * @param {Function} callback
*/ */
WalletDB.prototype.addBlock = co(function* addBlock(entry, txs, force) { WalletDB.prototype.addBlock = co(function* addBlock(entry, txs) {
var unlock = yield this._lockTX(force); var unlock = yield this.txLock.lock();
try {
return yield this._addBlock(entry, txs);
} finally {
unlock();
}
});
WalletDB.prototype._addBlock = co(function* addBlock(entry, txs) {
var i, block, matches, hash, tx, wallets; var i, block, matches, hash, tx, wallets;
if (this.options.useCheckpoints) { if (this.options.useCheckpoints) {
if (entry.height <= this.network.checkpoints.lastHeight) { if (entry.height <= this.network.checkpoints.lastHeight) {
try { yield this.setTip(entry.hash, entry.height);
yield this.setTip(entry.hash, entry.height);
} catch (e) {
unlock();
throw e;
}
unlock();
return; return;
} }
} }
@ -1266,12 +1243,7 @@ WalletDB.prototype.addBlock = co(function* addBlock(entry, txs, force) {
for (i = 0; i < txs.length; i++) { for (i = 0; i < txs.length; i++) {
tx = txs[i]; tx = txs[i];
try { wallets = yield this._addTX(tx);
wallets = yield this.addTX(tx, true);
} catch (e) {
unlock();
throw e;
}
if (!wallets) if (!wallets)
continue; continue;
@ -1286,14 +1258,7 @@ WalletDB.prototype.addBlock = co(function* addBlock(entry, txs, force) {
utils.revHex(block.hash), block.hashes.length); utils.revHex(block.hash), block.hashes.length);
} }
try { yield this.writeBlock(block, matches);
yield this.writeBlock(block, matches);
} catch (e) {
unlock();
throw e;
}
unlock();
}); });
/** /**
@ -1304,7 +1269,15 @@ WalletDB.prototype.addBlock = co(function* addBlock(entry, txs, force) {
*/ */
WalletDB.prototype.removeBlock = co(function* removeBlock(entry) { WalletDB.prototype.removeBlock = co(function* removeBlock(entry) {
var unlock = yield this._lockTX(); var unlock = yield this.txLock.lock();
try {
return yield this._removeBlock(entry, txs);
} finally {
unlock();
}
});
WalletDB.prototype._removeBlock = co(function* removeBlock(entry) {
var i, j, block, data, hash, wallets, wid, wallet; var i, j, block, data, hash, wallets, wid, wallet;
block = WalletBlock.fromEntry(entry); block = WalletBlock.fromEntry(entry);
@ -1349,8 +1322,6 @@ WalletDB.prototype.removeBlock = co(function* removeBlock(entry) {
this.tip = block.hash; this.tip = block.hash;
this.height = block.height; this.height = block.height;
unlock();
}); });
/** /**
@ -1362,7 +1333,15 @@ WalletDB.prototype.removeBlock = co(function* removeBlock(entry) {
*/ */
WalletDB.prototype.addTX = co(function* addTX(tx, force) { WalletDB.prototype.addTX = co(function* addTX(tx, force) {
var unlock = yield this._lockTX(force); var unlock = yield this.txLock.lock();
try {
return yield this._addTX(tx);
} finally {
unlock();
}
});
WalletDB.prototype._addTX = co(function* addTX(tx, force) {
var i, wallets, info, wallet; var i, wallets, info, wallet;
assert(!tx.mutable, 'Cannot add mutable TX to wallet.'); assert(!tx.mutable, 'Cannot add mutable TX to wallet.');
@ -1371,17 +1350,10 @@ WalletDB.prototype.addTX = co(function* addTX(tx, force) {
// Atomicity doesn't matter here. If we crash, // Atomicity doesn't matter here. If we crash,
// the automatic rescan will get the database // the automatic rescan will get the database
// back in the correct state. // back in the correct state.
try { wallets = yield this.mapWallets(tx);
wallets = yield this.mapWallets(tx);
} catch (e) {
unlock();
throw e;
}
if (!wallets) { if (!wallets)
unlock();
return; return;
}
this.logger.info( this.logger.info(
'Incoming transaction for %d wallets (%s).', 'Incoming transaction for %d wallets (%s).',
@ -1398,16 +1370,10 @@ WalletDB.prototype.addTX = co(function* addTX(tx, force) {
info.id = wallet.id; info.id = wallet.id;
try { yield wallet.tx.add(tx, info);
yield wallet.tx.add(tx, info); yield wallet.handleTX(info);
yield wallet.handleTX(info);
} catch (e) {
unlock();
throw e;
}
} }
unlock();
return wallets; return wallets;
}); });