refactor: locks.
This commit is contained in:
parent
8cb6faa078
commit
4e11bbbf9f
@ -235,16 +235,6 @@ Chain.prototype._close = function 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.
|
||||
* @private
|
||||
@ -929,23 +919,23 @@ Chain.prototype.setBestChain = co(function* setBestChain(entry, block, prev) {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Chain.prototype.reset = co(function* reset(height, force) {
|
||||
var unlock = yield this._lock(null, force);
|
||||
var result;
|
||||
|
||||
Chain.prototype.reset = co(function* reset(height) {
|
||||
var unlock = yield this.locker.lock();
|
||||
try {
|
||||
result = yield this.db.reset(height);
|
||||
} catch (e) {
|
||||
return yield this._reset(height);
|
||||
} finally {
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
Chain.prototype._reset = co(function* reset(height) {
|
||||
var result = yield this.db.reset(height);
|
||||
|
||||
// Reset the orphan map completely. There may
|
||||
// have been some orphans on a forked chain we
|
||||
// no longer need.
|
||||
this.purgeOrphans();
|
||||
|
||||
unlock();
|
||||
return result;
|
||||
});
|
||||
|
||||
@ -958,19 +948,23 @@ Chain.prototype.reset = co(function* reset(height, force) {
|
||||
*/
|
||||
|
||||
Chain.prototype.resetTime = co(function* resetTime(ts) {
|
||||
var unlock = yield this._lock();
|
||||
var entry = yield this.byTime(ts);
|
||||
|
||||
if (!entry)
|
||||
return unlock();
|
||||
|
||||
var unlock = yield this.locker.lock();
|
||||
try {
|
||||
yield this.reset(entry.height, true);
|
||||
return yield this._resetTime(ts);
|
||||
} finally {
|
||||
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
|
||||
* all of the blocks in its queue).
|
||||
@ -999,14 +993,23 @@ Chain.prototype.isBusy = function isBusy() {
|
||||
*/
|
||||
|
||||
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 existing, prev;
|
||||
|
||||
assert(this.loaded);
|
||||
|
||||
unlock = yield this._lock(block);
|
||||
|
||||
ret = new VerifyResult();
|
||||
initial = true;
|
||||
|
||||
@ -1014,23 +1017,18 @@ Chain.prototype.add = co(function* add(block) {
|
||||
hash = block.hash('hex');
|
||||
prevBlock = block.prevBlock;
|
||||
|
||||
this.currentBlock = hash;
|
||||
this._mark();
|
||||
|
||||
// Do not revalidate known invalid blocks.
|
||||
if (this.invalid[hash] || this.invalid[prevBlock]) {
|
||||
this.emit('invalid', block, block.getCoinbaseHeight());
|
||||
this.invalid[hash] = true;
|
||||
this.currentBlock = null;
|
||||
unlock();
|
||||
throw new VerifyError(block, 'duplicate', 'duplicate', 100);
|
||||
}
|
||||
|
||||
// Do we already have this block?
|
||||
if (this.hasPending(hash)) {
|
||||
this.emit('exists', block, block.getCoinbaseHeight());
|
||||
this.currentBlock = null;
|
||||
unlock();
|
||||
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.currentBlock = null;
|
||||
unlock();
|
||||
throw new VerifyError(block, 'invalid', 'bad-prevblk', 0);
|
||||
}
|
||||
|
||||
@ -1063,8 +1059,6 @@ Chain.prototype.add = co(function* add(block) {
|
||||
if (initial && !block.verify(ret)) {
|
||||
this.invalid[hash] = true;
|
||||
this.emit('invalid', block, block.getCoinbaseHeight());
|
||||
this.currentBlock = null;
|
||||
unlock();
|
||||
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?
|
||||
if (existing) {
|
||||
this.emit('exists', block, block.getCoinbaseHeight());
|
||||
this.currentBlock = null;
|
||||
unlock();
|
||||
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.currentBlock = null;
|
||||
unlock();
|
||||
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.currentBlock = null;
|
||||
unlock();
|
||||
throw new VerifyError(block,
|
||||
'checkpoint',
|
||||
'checkpoint mismatch',
|
||||
@ -1148,8 +1136,6 @@ Chain.prototype.add = co(function* add(block) {
|
||||
block = block.toBlock();
|
||||
} catch (e) {
|
||||
this.logger.error(e);
|
||||
this.currentBlock = null;
|
||||
unlock();
|
||||
throw new VerifyError(block,
|
||||
'malformed',
|
||||
'error parsing message',
|
||||
@ -1170,13 +1156,7 @@ Chain.prototype.add = co(function* add(block) {
|
||||
// our tip's. Add the block but do _not_
|
||||
// connect the inputs.
|
||||
if (entry.chainwork.cmp(this.tip.chainwork) <= 0) {
|
||||
try {
|
||||
yield this.db.save(entry, block, null, false);
|
||||
} catch (e) {
|
||||
this.currentBlock = null;
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
yield this.db.save(entry, block, null, false);
|
||||
|
||||
this.emit('competitor', block, entry);
|
||||
|
||||
@ -1184,13 +1164,7 @@ Chain.prototype.add = co(function* add(block) {
|
||||
this.emit('competitor resolved', block, entry);
|
||||
} else {
|
||||
// Attempt to add block to the chain index.
|
||||
try {
|
||||
yield this.setBestChain(entry, block, prev);
|
||||
} catch (e) {
|
||||
this.currentBlock = null;
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
yield this.setBestChain(entry, block, prev);
|
||||
|
||||
// Emit our block (and potentially resolved
|
||||
// orphan) only if it is on the main chain.
|
||||
@ -1228,10 +1202,6 @@ Chain.prototype.add = co(function* add(block) {
|
||||
this.synced = true;
|
||||
this.emit('full');
|
||||
}
|
||||
|
||||
this.currentBlock = null;
|
||||
|
||||
unlock();
|
||||
});
|
||||
|
||||
/**
|
||||
@ -1522,7 +1492,15 @@ Chain.prototype.getProgress = function getProgress() {
|
||||
*/
|
||||
|
||||
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 step = 1;
|
||||
var height, entry, main, hash;
|
||||
@ -1578,7 +1556,6 @@ Chain.prototype.getLocator = co(function* getLocator(start) {
|
||||
hash = entry.hash;
|
||||
}
|
||||
|
||||
unlock();
|
||||
return hashes;
|
||||
});
|
||||
|
||||
|
||||
@ -85,6 +85,7 @@ function Mempool(options) {
|
||||
this.orphans = {};
|
||||
this.tx = {};
|
||||
this.spents = {};
|
||||
this.currentTX = null;
|
||||
this.coinIndex = new AddressIndex(this);
|
||||
this.txIndex = new AddressIndex(this);
|
||||
|
||||
@ -136,16 +137,6 @@ Mempool.prototype._close = function close() {
|
||||
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
|
||||
* 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) {
|
||||
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 i, entry, tx, hash;
|
||||
|
||||
@ -190,7 +189,6 @@ Mempool.prototype.addBlock = co(function* addBlock(block) {
|
||||
this.rejects.reset();
|
||||
|
||||
yield spawn.wait();
|
||||
unlock();
|
||||
});
|
||||
|
||||
/**
|
||||
@ -202,6 +200,14 @@ Mempool.prototype.addBlock = co(function* addBlock(block) {
|
||||
|
||||
Mempool.prototype.removeBlock = co(function* removeBlock(block) {
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
try {
|
||||
yield this.addUnchecked(entry, true);
|
||||
} catch (e) {
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
yield this._addUnchecked(entry);
|
||||
|
||||
this.emit('unconfirmed', tx, block);
|
||||
}
|
||||
|
||||
this.rejects.reset();
|
||||
|
||||
unlock();
|
||||
});
|
||||
|
||||
/**
|
||||
@ -503,6 +502,9 @@ Mempool.prototype.has = function has(hash) {
|
||||
if (this.hasOrphan(hash))
|
||||
return true;
|
||||
|
||||
if (hash === this.currentTX)
|
||||
return true;
|
||||
|
||||
return this.hasTX(hash);
|
||||
};
|
||||
|
||||
@ -526,9 +528,11 @@ Mempool.prototype.hasReject = function hasReject(hash) {
|
||||
*/
|
||||
|
||||
Mempool.prototype.addTX = co(function* addTX(tx) {
|
||||
var unlock = yield this._lock(tx);
|
||||
var unlock = yield this.locker.lock(tx);
|
||||
var missing;
|
||||
|
||||
this.currentTX = tx.hash('hex');
|
||||
|
||||
try {
|
||||
missing = yield this._addTX(tx);
|
||||
} catch (err) {
|
||||
@ -536,10 +540,12 @@ Mempool.prototype.addTX = co(function* addTX(tx) {
|
||||
if (!tx.hasWitness() && !err.malleated)
|
||||
this.rejects.add(tx.hash());
|
||||
}
|
||||
this.currentTX = null;
|
||||
unlock();
|
||||
throw err;
|
||||
}
|
||||
|
||||
this.currentTX = null;
|
||||
unlock();
|
||||
return missing;
|
||||
});
|
||||
@ -651,7 +657,7 @@ Mempool.prototype._addTX = co(function* _addTX(tx) {
|
||||
entry = MempoolEntry.fromTX(tx, this.chain.height);
|
||||
|
||||
yield this.verify(entry);
|
||||
yield this.addUnchecked(entry, true);
|
||||
yield this._addUnchecked(entry);
|
||||
|
||||
if (this.limitMempoolSize(hash)) {
|
||||
throw new VerifyError(tx,
|
||||
@ -671,8 +677,16 @@ Mempool.prototype._addTX = co(function* _addTX(tx) {
|
||||
* @param {Function} callback - Returns [{@link VerifyError}].
|
||||
*/
|
||||
|
||||
Mempool.prototype.addUnchecked = co(function* addUnchecked(entry, force) {
|
||||
var unlock = yield this._lock(null, force);
|
||||
Mempool.prototype.addUnchecked = co(function* addUnchecked(entry) {
|
||||
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;
|
||||
|
||||
this.trackEntry(entry);
|
||||
@ -709,7 +723,7 @@ Mempool.prototype.addUnchecked = co(function* addUnchecked(entry, force) {
|
||||
}
|
||||
|
||||
try {
|
||||
yield this.addUnchecked(orphan, true);
|
||||
yield this._addUnchecked(orphan);
|
||||
} catch (err) {
|
||||
this.emit('error', err);
|
||||
continue;
|
||||
@ -717,8 +731,6 @@ Mempool.prototype.addUnchecked = co(function* addUnchecked(entry, force) {
|
||||
|
||||
this.logger.spam('Resolved orphan %s in mempool.', orphan.tx.rhash);
|
||||
}
|
||||
|
||||
unlock();
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@ -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
|
||||
* if a socket was passed into peer).
|
||||
@ -1120,20 +1111,28 @@ Peer.prototype._handleUTXOs = function _handleUTXOs(utxos) {
|
||||
*/
|
||||
|
||||
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;
|
||||
|
||||
if (!this.chain.synced)
|
||||
return unlock();
|
||||
return;
|
||||
|
||||
if (this.options.selfish)
|
||||
return unlock();
|
||||
return;
|
||||
|
||||
if (this.chain.db.options.spv)
|
||||
return unlock();
|
||||
return;
|
||||
|
||||
if (packet.prevout.length > 15)
|
||||
return unlock();
|
||||
return;
|
||||
|
||||
utxos = new packets.GetUTXOsPacket();
|
||||
|
||||
@ -1182,7 +1181,6 @@ Peer.prototype._handleGetUTXOs = co(function* _handleGetUTXOs(packet) {
|
||||
utxos.tip = this.chain.tip.hash;
|
||||
|
||||
this.send(utxos);
|
||||
unlock();
|
||||
});
|
||||
|
||||
/**
|
||||
@ -1203,21 +1201,29 @@ Peer.prototype._handleHaveWitness = function _handleHaveWitness(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 hash, entry;
|
||||
|
||||
if (!this.chain.synced)
|
||||
return unlock();
|
||||
return;
|
||||
|
||||
if (this.options.selfish)
|
||||
return unlock();
|
||||
return;
|
||||
|
||||
if (this.chain.db.options.spv)
|
||||
return unlock();
|
||||
return;
|
||||
|
||||
if (this.chain.db.options.prune)
|
||||
return unlock();
|
||||
return;
|
||||
|
||||
if (packet.locator.length > 0) {
|
||||
hash = yield this.chain.findLocator(packet.locator);
|
||||
@ -1243,8 +1249,6 @@ Peer.prototype._handleGetHeaders = co(function* _handleGetHeaders(packet) {
|
||||
}
|
||||
|
||||
this.sendHeaders(headers);
|
||||
|
||||
unlock();
|
||||
});
|
||||
|
||||
/**
|
||||
@ -1254,21 +1258,29 @@ Peer.prototype._handleGetHeaders = co(function* _handleGetHeaders(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 hash;
|
||||
|
||||
if (!this.chain.synced)
|
||||
return unlock();
|
||||
return;
|
||||
|
||||
if (this.options.selfish)
|
||||
return unlock();
|
||||
return;
|
||||
|
||||
if (this.chain.db.options.spv)
|
||||
return unlock();
|
||||
return;
|
||||
|
||||
if (this.chain.db.options.prune)
|
||||
return unlock();
|
||||
return;
|
||||
|
||||
hash = yield this.chain.findLocator(packet.locator);
|
||||
|
||||
@ -1290,7 +1302,6 @@ Peer.prototype._handleGetBlocks = co(function* _handleGetBlocks(packet) {
|
||||
}
|
||||
|
||||
this.sendInv(blocks);
|
||||
unlock();
|
||||
});
|
||||
|
||||
/**
|
||||
@ -1476,7 +1487,15 @@ Peer.prototype._getItem = co(function* _getItem(item) {
|
||||
*/
|
||||
|
||||
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 items = packet.items;
|
||||
var i, j, item, entry, tx, block;
|
||||
@ -1580,8 +1599,6 @@ Peer.prototype._handleGetData = co(function* _handleGetData(packet) {
|
||||
|
||||
if (notFound.length > 0)
|
||||
this.send(new packets.NotFoundPacket(notFound));
|
||||
|
||||
unlock();
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@ -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.
|
||||
* @alias Pool#open
|
||||
@ -722,13 +713,20 @@ Pool.prototype.stopSync = function stopSync() {
|
||||
*/
|
||||
|
||||
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)
|
||||
return;
|
||||
|
||||
unlock = yield this._lock();
|
||||
|
||||
ret = new VerifyResult();
|
||||
|
||||
this.logger.debug(
|
||||
@ -751,13 +749,11 @@ Pool.prototype._handleHeaders = co(function* _handleHeaders(headers, peer) {
|
||||
|
||||
if (last && header.prevBlock !== last) {
|
||||
peer.setMisbehavior(100);
|
||||
unlock();
|
||||
throw new Error('Bad header chain.');
|
||||
}
|
||||
|
||||
if (!header.verify(ret)) {
|
||||
peer.reject(header, 'invalid', ret.reason, 100);
|
||||
unlock();
|
||||
throw new Error('Invalid header.');
|
||||
}
|
||||
|
||||
@ -778,8 +774,6 @@ Pool.prototype._handleHeaders = co(function* _handleHeaders(headers, peer) {
|
||||
// the peer's chain.
|
||||
if (last && headers.length === 2000)
|
||||
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) {
|
||||
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;
|
||||
|
||||
// Ignore for now if we're still syncing
|
||||
@ -868,7 +870,6 @@ Pool.prototype._handleInv = co(function* _handleInv(hashes, peer) {
|
||||
|
||||
if (!this.options.headers) {
|
||||
yield this._handleBlocks(hashes, peer);
|
||||
unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -878,7 +879,6 @@ Pool.prototype._handleInv = co(function* _handleInv(hashes, peer) {
|
||||
}
|
||||
|
||||
this.scheduleRequests(peer);
|
||||
unlock();
|
||||
});
|
||||
|
||||
/**
|
||||
@ -1048,7 +1048,7 @@ Pool.prototype.createPeer = function createPeer(addr, socket) {
|
||||
self.addLoader();
|
||||
});
|
||||
|
||||
peer.on('merkleblock', function(block) {
|
||||
peer.on('merkleblock', co(function *(block) {
|
||||
if (!self.options.spv)
|
||||
return;
|
||||
|
||||
@ -1057,17 +1057,20 @@ Pool.prototype.createPeer = function createPeer(addr, socket) {
|
||||
|
||||
// If the peer sent us a block that was added
|
||||
// to the chain (not orphans), reset the timeout.
|
||||
self._handleBlock(block, peer).then(function() {
|
||||
if (peer.isLoader()) {
|
||||
self.startInterval();
|
||||
self.startTimeout();
|
||||
}
|
||||
}, function(err) {
|
||||
try {
|
||||
yield self._handleBlock(block, peer);
|
||||
} catch (e) {
|
||||
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)
|
||||
return;
|
||||
|
||||
@ -1076,15 +1079,18 @@ Pool.prototype.createPeer = function createPeer(addr, socket) {
|
||||
|
||||
// If the peer sent us a block that was added
|
||||
// to the chain (not orphans), reset the timeout.
|
||||
self._handleBlock(block, peer).then(function() {
|
||||
if (peer.isLoader()) {
|
||||
self.startInterval();
|
||||
self.startTimeout();
|
||||
}
|
||||
}, function(err) {
|
||||
self.emit('error', err);
|
||||
});
|
||||
});
|
||||
try {
|
||||
yield self._handleBlock(block, peer);
|
||||
} catch (e) {
|
||||
self.emit('error', e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (peer.isLoader()) {
|
||||
self.startInterval();
|
||||
self.startTimeout();
|
||||
}
|
||||
}));
|
||||
|
||||
peer.on('error', function(err) {
|
||||
self.emit('error', err, peer);
|
||||
@ -1620,21 +1626,18 @@ Pool.prototype.exists = function exists(type, hash) {
|
||||
* @param {Peer} peer
|
||||
*/
|
||||
|
||||
Pool.prototype.scheduleRequests = function scheduleRequests(peer) {
|
||||
var self = this;
|
||||
|
||||
Pool.prototype.scheduleRequests = co(function* scheduleRequests(peer) {
|
||||
if (this.scheduled)
|
||||
return;
|
||||
|
||||
this.scheduled = true;
|
||||
|
||||
this.chain.onDrain().then(function() {
|
||||
utils.nextTick(function() {
|
||||
self.sendRequests(peer);
|
||||
self.scheduled = false;
|
||||
});
|
||||
});
|
||||
};
|
||||
yield this.chain.onDrain();
|
||||
yield spawn.wait();
|
||||
|
||||
this.sendRequests(peer);
|
||||
this.scheduled = false;
|
||||
});
|
||||
|
||||
/**
|
||||
* Send scheduled requests in the request queues.
|
||||
|
||||
@ -253,16 +253,6 @@ TXDB.prototype.emit = function 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.
|
||||
* @param {Buffer} key
|
||||
@ -594,7 +584,15 @@ TXDB.prototype._resolveOrphans = co(function* _resolveOrphans(tx, index) {
|
||||
*/
|
||||
|
||||
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 i, result, input, output, coin;
|
||||
var prevout, key, address, spender, orphans;
|
||||
@ -605,18 +603,14 @@ TXDB.prototype.add = co(function* add(tx, info) {
|
||||
result = yield this._confirm(tx, info);
|
||||
|
||||
// Ignore if we already have this tx.
|
||||
if (result) {
|
||||
unlock();
|
||||
if (result)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Verify and get coins.
|
||||
result = yield this._verify(tx, info);
|
||||
|
||||
if (!result) {
|
||||
unlock();
|
||||
if (!result)
|
||||
return false;
|
||||
}
|
||||
|
||||
hash = tx.hash('hex');
|
||||
|
||||
@ -667,7 +661,6 @@ TXDB.prototype.add = co(function* add(tx, info) {
|
||||
yield this._addOrphan(prevout, spender);
|
||||
} catch (e) {
|
||||
this.drop();
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
continue;
|
||||
@ -697,7 +690,6 @@ TXDB.prototype.add = co(function* add(tx, info) {
|
||||
orphans = yield this._resolveOrphans(tx, i);
|
||||
} catch (e) {
|
||||
this.drop();
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
|
||||
@ -714,12 +706,7 @@ TXDB.prototype.add = co(function* add(tx, info) {
|
||||
this.coinCache.set(key, coin);
|
||||
}
|
||||
|
||||
try {
|
||||
yield this.commit();
|
||||
} catch (e) {
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
yield this.commit();
|
||||
|
||||
// Clear any locked coins to free up memory.
|
||||
this.unlockTX(tx);
|
||||
@ -729,7 +716,6 @@ TXDB.prototype.add = co(function* add(tx, info) {
|
||||
if (tx.ts !== 0)
|
||||
this.emit('confirmed', tx, info);
|
||||
|
||||
unlock();
|
||||
return true;
|
||||
});
|
||||
|
||||
@ -964,11 +950,23 @@ TXDB.prototype._confirm = co(function* _confirm(tx, info) {
|
||||
* @param {Function} callback - Returns [Error].
|
||||
*/
|
||||
|
||||
TXDB.prototype.remove = co(function* remove(hash, force) {
|
||||
var unlock = yield this._lock(force);
|
||||
var info = yield this._removeRecursive();
|
||||
TXDB.prototype.remove = co(function* remove(hash) {
|
||||
var unlock = yield this.locker.lock();
|
||||
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)
|
||||
return;
|
||||
@ -1087,33 +1085,26 @@ TXDB.prototype._remove = co(function* remove(tx, info) {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
TXDB.prototype.unconfirm = co(function* unconfirm(hash, force) {
|
||||
var unlock = yield this._lock(force);
|
||||
var tx, info, result;
|
||||
|
||||
TXDB.prototype.unconfirm = co(function* unconfirm(hash) {
|
||||
var unlock = yield this.locker.lock();
|
||||
try {
|
||||
tx = yield this.getTX(hash);
|
||||
} catch (e) {
|
||||
return yield this.unconfirml(hash);
|
||||
} finally {
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
if (!tx) {
|
||||
unlock();
|
||||
TXDB.prototype.unconfirml = co(function* unconfirm(hash) {
|
||||
var tx = yield this.getTX(hash);
|
||||
var info, result;
|
||||
|
||||
if (!tx)
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
info = yield this.getInfo(tx);
|
||||
} catch (e) {
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
info = yield this.getInfo(tx);
|
||||
|
||||
if (!info) {
|
||||
unlock();
|
||||
if (!info)
|
||||
return false;
|
||||
}
|
||||
|
||||
this.start();
|
||||
|
||||
@ -1121,18 +1112,11 @@ TXDB.prototype.unconfirm = co(function* unconfirm(hash, force) {
|
||||
result = yield this._unconfirm(tx, info);
|
||||
} catch (e) {
|
||||
this.drop();
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
|
||||
try {
|
||||
yield this.commit();
|
||||
} catch (e) {
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
yield this.commit();
|
||||
|
||||
unlock();
|
||||
return result;
|
||||
});
|
||||
|
||||
@ -1943,7 +1927,15 @@ TXDB.prototype.getAccountBalance = co(function* getBalance(account) {
|
||||
*/
|
||||
|
||||
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;
|
||||
|
||||
if (!utils.isUInt32(age))
|
||||
@ -1961,15 +1953,8 @@ TXDB.prototype.zap = co(function* zap(account, age) {
|
||||
if (tx.ts !== 0)
|
||||
continue;
|
||||
|
||||
try {
|
||||
yield this.remove(hash, true);
|
||||
} catch (e) {
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
yield this.removel(hash);
|
||||
}
|
||||
|
||||
unlock();
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@ -77,24 +77,6 @@ function Wallet(db, options) {
|
||||
|
||||
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.
|
||||
* @private
|
||||
@ -246,7 +228,15 @@ Wallet.prototype.destroy = function destroy() {
|
||||
*/
|
||||
|
||||
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;
|
||||
|
||||
if (!key) {
|
||||
@ -259,10 +249,8 @@ Wallet.prototype.addKey = co(function* addKey(account, key) {
|
||||
|
||||
account = yield this.getAccount(account);
|
||||
|
||||
if (!account) {
|
||||
unlock();
|
||||
if (!account)
|
||||
throw new Error('Account not found.');
|
||||
}
|
||||
|
||||
this.start();
|
||||
|
||||
@ -270,13 +258,11 @@ Wallet.prototype.addKey = co(function* addKey(account, key) {
|
||||
result = yield account.addKey(key, true);
|
||||
} catch (e) {
|
||||
this.drop();
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
|
||||
yield this.commit();
|
||||
|
||||
unlock();
|
||||
return result;
|
||||
});
|
||||
|
||||
@ -288,7 +274,15 @@ Wallet.prototype.addKey = co(function* addKey(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;
|
||||
|
||||
if (!key) {
|
||||
@ -301,10 +295,8 @@ Wallet.prototype.removeKey = co(function* removeKey(account, key) {
|
||||
|
||||
account = yield this.getAccount(account);
|
||||
|
||||
if (!account) {
|
||||
unlock();
|
||||
if (!account)
|
||||
throw new Error('Account not found.');
|
||||
}
|
||||
|
||||
this.start();
|
||||
|
||||
@ -312,13 +304,11 @@ Wallet.prototype.removeKey = co(function* removeKey(account, key) {
|
||||
result = yield account.removeKey(key, true);
|
||||
} catch (e) {
|
||||
this.drop();
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
|
||||
yield this.commit();
|
||||
|
||||
unlock();
|
||||
return result;
|
||||
});
|
||||
|
||||
@ -330,27 +320,30 @@ Wallet.prototype.removeKey = co(function* removeKey(account, key) {
|
||||
*/
|
||||
|
||||
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;
|
||||
old = null;
|
||||
}
|
||||
|
||||
try {
|
||||
if (old)
|
||||
yield this.master.decrypt(old);
|
||||
if (new_)
|
||||
yield this.master.encrypt(new_);
|
||||
} catch (e) {
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
if (old)
|
||||
yield this.master.decrypt(old);
|
||||
|
||||
if (new_)
|
||||
yield this.master.encrypt(new_);
|
||||
|
||||
this.start();
|
||||
this.save();
|
||||
|
||||
yield this.commit();
|
||||
unlock();
|
||||
});
|
||||
|
||||
/**
|
||||
@ -360,24 +353,25 @@ Wallet.prototype.setPassphrase = co(function* setPassphrase(old, new_) {
|
||||
*/
|
||||
|
||||
Wallet.prototype.retoken = co(function* retoken(passphrase) {
|
||||
var unlock = yield this._lockWrite();
|
||||
var master;
|
||||
|
||||
var unlock = yield this.writeLock.lock();
|
||||
try {
|
||||
master = yield this.unlock(passphrase);
|
||||
} catch (e) {
|
||||
return yield this._retoken(passphrase);
|
||||
} finally {
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
Wallet.prototype._retoken = co(function* retoken(passphrase) {
|
||||
var master = yield this.unlock(passphrase);
|
||||
|
||||
this.tokenDepth++;
|
||||
this.token = this.getToken(master, this.tokenDepth);
|
||||
|
||||
this.start();
|
||||
this.save();
|
||||
|
||||
yield this.commit();
|
||||
|
||||
unlock();
|
||||
return this.token;
|
||||
});
|
||||
|
||||
@ -461,7 +455,15 @@ Wallet.prototype.getToken = function getToken(master, nonce) {
|
||||
*/
|
||||
|
||||
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 timeout = options.timeout;
|
||||
var name = options.name;
|
||||
@ -473,12 +475,7 @@ Wallet.prototype.createAccount = co(function* createAccount(options) {
|
||||
if (!name)
|
||||
name = this.accountDepth + '';
|
||||
|
||||
try {
|
||||
master = yield this.unlock(passphrase, timeout);
|
||||
} catch (e) {
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
master = yield this.unlock(passphrase, timeout);
|
||||
|
||||
key = master.deriveAccount44(this.accountDepth);
|
||||
|
||||
@ -502,14 +499,13 @@ Wallet.prototype.createAccount = co(function* createAccount(options) {
|
||||
account = yield this.db.createAccount(options);
|
||||
} catch (e) {
|
||||
this.drop();
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
|
||||
this.accountDepth++;
|
||||
this.save();
|
||||
|
||||
yield this.commit();
|
||||
unlock();
|
||||
|
||||
return account;
|
||||
});
|
||||
@ -614,7 +610,15 @@ Wallet.prototype.createChange = function createChange(account) {
|
||||
*/
|
||||
|
||||
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;
|
||||
|
||||
if (typeof account === 'boolean') {
|
||||
@ -625,30 +629,21 @@ Wallet.prototype.createAddress = co(function* createAddress(account, change) {
|
||||
if (account == null)
|
||||
account = 0;
|
||||
|
||||
try {
|
||||
account = yield this.getAccount(account);
|
||||
} catch (e) {
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
account = yield this.getAccount(account);
|
||||
|
||||
if (!account) {
|
||||
unlock();
|
||||
if (!account)
|
||||
throw new Error('Account not found.');
|
||||
}
|
||||
|
||||
this.start();
|
||||
|
||||
try {
|
||||
result = yield account.createAddress(change, true);
|
||||
result = yield account.createAddress(change);
|
||||
} catch (e) {
|
||||
this.drop();
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
|
||||
yield this.commit();
|
||||
unlock();
|
||||
|
||||
return result;
|
||||
});
|
||||
@ -760,7 +755,15 @@ Wallet.prototype.getPaths = co(function* getPaths(account) {
|
||||
*/
|
||||
|
||||
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;
|
||||
|
||||
if (account && typeof account === 'object') {
|
||||
@ -772,36 +775,20 @@ Wallet.prototype.importKey = co(function* importKey(account, ring, passphrase) {
|
||||
if (account == null)
|
||||
account = 0;
|
||||
|
||||
try {
|
||||
exists = yield this.getPath(ring.getHash('hex'));
|
||||
} catch (e) {
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
exists = yield this.getPath(ring.getHash('hex'));
|
||||
|
||||
if (exists) {
|
||||
unlock();
|
||||
if (exists)
|
||||
throw new Error('Key already exists.');
|
||||
}
|
||||
|
||||
account = yield this.getAccount(account);
|
||||
|
||||
if (!account) {
|
||||
unlock();
|
||||
if (!account)
|
||||
throw new Error('Account not found.');
|
||||
}
|
||||
|
||||
if (account.type !== bcoin.account.types.PUBKEYHASH) {
|
||||
unlock();
|
||||
if (account.type !== bcoin.account.types.PUBKEYHASH)
|
||||
throw new Error('Cannot import into non-pkh account.');
|
||||
}
|
||||
|
||||
try {
|
||||
yield this.unlock(passphrase);
|
||||
} catch (e) {
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
yield this.unlock(passphrase);
|
||||
|
||||
raw = ring.toRaw();
|
||||
path = Path.fromAccount(account, ring);
|
||||
@ -821,12 +808,10 @@ Wallet.prototype.importKey = co(function* importKey(account, ring, passphrase) {
|
||||
yield account.saveAddress([ring], true);
|
||||
} catch (e) {
|
||||
this.drop();
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
|
||||
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) {
|
||||
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;
|
||||
|
||||
if (!options)
|
||||
options = {};
|
||||
|
||||
if (!this.initialized) {
|
||||
unlock();
|
||||
if (!this.initialized)
|
||||
throw new Error('Wallet is not initialized.');
|
||||
}
|
||||
|
||||
if (options.account != null) {
|
||||
account = yield this.getAccount(options.account);
|
||||
if (!account) {
|
||||
unlock();
|
||||
if (!account)
|
||||
throw new Error('Account not found.');
|
||||
}
|
||||
} else {
|
||||
account = this.account;
|
||||
}
|
||||
|
||||
if (!account.initialized) {
|
||||
unlock();
|
||||
if (!account.initialized)
|
||||
throw new Error('Account is not initialized.');
|
||||
}
|
||||
|
||||
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.
|
||||
coins = this.tx.filterLocked(coins);
|
||||
|
||||
try {
|
||||
tx.fund(coins, {
|
||||
selection: options.selection,
|
||||
round: options.round,
|
||||
confirmations: options.confirmations,
|
||||
free: options.free,
|
||||
hardFee: options.hardFee,
|
||||
subtractFee: options.subtractFee,
|
||||
changeAddress: account.changeAddress.getAddress(),
|
||||
height: this.db.height,
|
||||
rate: rate,
|
||||
maxFee: options.maxFee,
|
||||
m: account.m,
|
||||
n: account.n,
|
||||
witness: account.witness,
|
||||
script: account.receiveAddress.script
|
||||
});
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
tx.fund(coins, {
|
||||
selection: options.selection,
|
||||
round: options.round,
|
||||
confirmations: options.confirmations,
|
||||
free: options.free,
|
||||
hardFee: options.hardFee,
|
||||
subtractFee: options.subtractFee,
|
||||
changeAddress: account.changeAddress.getAddress(),
|
||||
height: this.db.height,
|
||||
rate: rate,
|
||||
maxFee: options.maxFee,
|
||||
m: account.m,
|
||||
n: account.n,
|
||||
witness: account.witness,
|
||||
script: account.receiveAddress.script
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
@ -977,27 +960,21 @@ Wallet.prototype.createTX = co(function* createTX(options, force) {
|
||||
*/
|
||||
|
||||
Wallet.prototype.send = co(function* send(options) {
|
||||
var unlock = yield this._lockFund();
|
||||
var tx;
|
||||
|
||||
var unlock = yield this.fundLock.lock();
|
||||
try {
|
||||
tx = yield this.createTX(options, true);
|
||||
} catch (e) {
|
||||
return yield this._send(options);
|
||||
} finally {
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
yield this.sign(tx);
|
||||
} catch (e) {
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
Wallet.prototype._send = co(function* send(options) {
|
||||
var tx = yield this.createTX(options, true);
|
||||
|
||||
if (!tx.isSigned()) {
|
||||
unlock();
|
||||
yield this.sign(tx);
|
||||
|
||||
if (!tx.isSigned())
|
||||
throw new Error('TX could not be fully signed.');
|
||||
}
|
||||
|
||||
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.db.emit('send', tx);
|
||||
|
||||
unlock();
|
||||
return tx;
|
||||
});
|
||||
|
||||
@ -1160,7 +1136,15 @@ Wallet.prototype.getOutputPaths = co(function* getOutputPaths(tx) {
|
||||
*/
|
||||
|
||||
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 accounts = {};
|
||||
var i, j, path, paths, account;
|
||||
@ -1204,22 +1188,12 @@ Wallet.prototype.syncOutputDepth = co(function* syncOutputDepth(info) {
|
||||
receiveDepth += 2;
|
||||
changeDepth += 2;
|
||||
|
||||
try {
|
||||
account = yield this.getAccount(account);
|
||||
} catch (e) {
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
account = yield this.getAccount(account);
|
||||
|
||||
if (!account)
|
||||
continue;
|
||||
|
||||
try {
|
||||
ret = yield account.setDepth(receiveDepth, changeDepth);
|
||||
} catch (e) {
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
ret = yield account.setDepth(receiveDepth, changeDepth);
|
||||
|
||||
rcv = ret[0];
|
||||
chng = ret[1];
|
||||
@ -1235,7 +1209,6 @@ Wallet.prototype.syncOutputDepth = co(function* syncOutputDepth(info) {
|
||||
|
||||
yield this.commit();
|
||||
|
||||
unlock();
|
||||
return receive;
|
||||
});
|
||||
|
||||
@ -2002,15 +1975,6 @@ MasterKey.fromOptions = function 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.
|
||||
* @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) {
|
||||
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;
|
||||
|
||||
if (this.key) {
|
||||
unlock();
|
||||
if (this.key)
|
||||
return this.key;
|
||||
}
|
||||
|
||||
if (!passphrase) {
|
||||
unlock();
|
||||
if (!passphrase)
|
||||
throw new Error('No passphrase.');
|
||||
}
|
||||
|
||||
assert(this.encrypted);
|
||||
|
||||
try {
|
||||
key = yield crypto.derive(passphrase);
|
||||
data = crypto.decipher(this.ciphertext, key, this.iv);
|
||||
} catch (e) {
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
key = yield crypto.derive(passphrase);
|
||||
data = crypto.decipher(this.ciphertext, key, this.iv);
|
||||
|
||||
this.key = bcoin.hd.fromExtended(data);
|
||||
|
||||
@ -2048,7 +2011,6 @@ MasterKey.prototype.unlock = co(function* _unlock(passphrase, timeout) {
|
||||
|
||||
this.aesKey = key;
|
||||
|
||||
unlock();
|
||||
return this.key;
|
||||
});
|
||||
|
||||
@ -2137,38 +2099,33 @@ MasterKey.prototype.destroy = function destroy() {
|
||||
*/
|
||||
|
||||
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;
|
||||
|
||||
if (!this.encrypted) {
|
||||
assert(this.key);
|
||||
return unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!passphrase)
|
||||
return unlock();
|
||||
return;
|
||||
|
||||
this.destroy();
|
||||
|
||||
try {
|
||||
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;
|
||||
}
|
||||
data = yield crypto.decrypt(this.ciphertext, passphrase, this.iv);
|
||||
|
||||
this.key = bcoin.hd.fromExtended(data);
|
||||
this.encrypted = false;
|
||||
this.iv = null;
|
||||
this.ciphertext = null;
|
||||
|
||||
unlock();
|
||||
});
|
||||
|
||||
/**
|
||||
@ -2178,33 +2135,34 @@ MasterKey.prototype.decrypt = co(function* decrypt(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;
|
||||
|
||||
if (this.encrypted)
|
||||
return unlock();
|
||||
return;
|
||||
|
||||
if (!passphrase)
|
||||
return unlock();
|
||||
return;
|
||||
|
||||
data = this.key.toExtended();
|
||||
iv = crypto.randomBytes(16);
|
||||
|
||||
this.stop();
|
||||
|
||||
try {
|
||||
data = yield crypto.encrypt(data, passphrase, iv);
|
||||
} catch (e) {
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
data = yield crypto.encrypt(data, passphrase, iv);
|
||||
|
||||
this.key = null;
|
||||
this.encrypted = true;
|
||||
this.iv = iv;
|
||||
this.ciphertext = data;
|
||||
|
||||
unlock();
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@ -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.
|
||||
* @alias WalletDB#open
|
||||
@ -474,7 +447,6 @@ WalletDB.prototype.getWalletID = function getWalletID(id) {
|
||||
*/
|
||||
|
||||
WalletDB.prototype.get = co(function* get(wid) {
|
||||
var self = this;
|
||||
var wallet, unlock;
|
||||
|
||||
wid = yield this.getWalletID(wid);
|
||||
@ -486,31 +458,30 @@ WalletDB.prototype.get = co(function* get(wid) {
|
||||
return wallet;
|
||||
|
||||
// NOTE: Lock must start here!
|
||||
unlock = yield this._lockRead(wid);
|
||||
unlock = yield this.readLock.lock(wid);
|
||||
|
||||
try {
|
||||
wallet = yield this.db.fetch(layout.w(wid), function(data) {
|
||||
return bcoin.wallet.fromRaw(self, data);
|
||||
});
|
||||
} catch (e) {
|
||||
return yield this._get(wid);
|
||||
} finally {
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
if (!wallet) {
|
||||
unlock();
|
||||
WalletDB.prototype._get = co(function* get(wid) {
|
||||
var self = this;
|
||||
var wallet;
|
||||
|
||||
wallet = yield this.db.fetch(layout.w(wid), function(data) {
|
||||
return bcoin.wallet.fromRaw(self, data);
|
||||
});
|
||||
|
||||
if (!wallet)
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.register(wallet);
|
||||
yield wallet.open();
|
||||
} catch (e) {
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
this.register(wallet);
|
||||
|
||||
yield wallet.open();
|
||||
|
||||
unlock();
|
||||
return wallet;
|
||||
});
|
||||
|
||||
@ -561,33 +532,36 @@ WalletDB.prototype.auth = co(function* auth(wid, token) {
|
||||
*/
|
||||
|
||||
WalletDB.prototype.create = co(function* create(options) {
|
||||
var unlock, wallet, exists;
|
||||
var unlock;
|
||||
|
||||
if (!options)
|
||||
options = {};
|
||||
|
||||
unlock = yield this._lockWrite(options.id);
|
||||
|
||||
exists = yield this.has(options.id);
|
||||
|
||||
if (exists) {
|
||||
unlock();
|
||||
throw new Error('Wallet already exists.');
|
||||
}
|
||||
unlock = yield this.writeLock.lock(options.id);
|
||||
|
||||
try {
|
||||
wallet = bcoin.wallet.fromOptions(this, options);
|
||||
wallet.wid = this.depth++;
|
||||
this.register(wallet);
|
||||
yield wallet.init(options);
|
||||
} catch (e) {
|
||||
return yield this._create(options);
|
||||
} finally {
|
||||
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);
|
||||
|
||||
unlock();
|
||||
return wallet;
|
||||
});
|
||||
|
||||
@ -963,8 +937,16 @@ WalletDB.prototype.getWallets = function getWallets() {
|
||||
*/
|
||||
|
||||
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 unlock = yield this._lockTX();
|
||||
var hashes;
|
||||
|
||||
if (height == null)
|
||||
@ -974,16 +956,9 @@ WalletDB.prototype.rescan = co(function* rescan(chaindb, height) {
|
||||
|
||||
this.logger.info('Scanning for %d addresses.', hashes.length);
|
||||
|
||||
try {
|
||||
yield chaindb.scan(height, hashes, co(function *(block, txs) {
|
||||
yield self.addBlock(block, txs, true);
|
||||
}));
|
||||
} catch (e) {
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
|
||||
unlock();
|
||||
yield chaindb.scan(height, hashes, co(function *(block, txs) {
|
||||
yield self._addBlock(block, txs);
|
||||
}));
|
||||
});
|
||||
|
||||
/**
|
||||
@ -1235,19 +1210,21 @@ WalletDB.prototype.getWalletsByTX = function getWalletsByTX(hash) {
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
WalletDB.prototype.addBlock = co(function* addBlock(entry, txs, force) {
|
||||
var unlock = yield this._lockTX(force);
|
||||
WalletDB.prototype.addBlock = co(function* addBlock(entry, txs) {
|
||||
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;
|
||||
|
||||
if (this.options.useCheckpoints) {
|
||||
if (entry.height <= this.network.checkpoints.lastHeight) {
|
||||
try {
|
||||
yield this.setTip(entry.hash, entry.height);
|
||||
} catch (e) {
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
unlock();
|
||||
yield this.setTip(entry.hash, entry.height);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1266,12 +1243,7 @@ WalletDB.prototype.addBlock = co(function* addBlock(entry, txs, force) {
|
||||
for (i = 0; i < txs.length; i++) {
|
||||
tx = txs[i];
|
||||
|
||||
try {
|
||||
wallets = yield this.addTX(tx, true);
|
||||
} catch (e) {
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
wallets = yield this._addTX(tx);
|
||||
|
||||
if (!wallets)
|
||||
continue;
|
||||
@ -1286,14 +1258,7 @@ WalletDB.prototype.addBlock = co(function* addBlock(entry, txs, force) {
|
||||
utils.revHex(block.hash), block.hashes.length);
|
||||
}
|
||||
|
||||
try {
|
||||
yield this.writeBlock(block, matches);
|
||||
} catch (e) {
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
|
||||
unlock();
|
||||
yield this.writeBlock(block, matches);
|
||||
});
|
||||
|
||||
/**
|
||||
@ -1304,7 +1269,15 @@ WalletDB.prototype.addBlock = co(function* addBlock(entry, txs, force) {
|
||||
*/
|
||||
|
||||
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;
|
||||
|
||||
block = WalletBlock.fromEntry(entry);
|
||||
@ -1349,8 +1322,6 @@ WalletDB.prototype.removeBlock = co(function* removeBlock(entry) {
|
||||
|
||||
this.tip = block.hash;
|
||||
this.height = block.height;
|
||||
|
||||
unlock();
|
||||
});
|
||||
|
||||
/**
|
||||
@ -1362,7 +1333,15 @@ WalletDB.prototype.removeBlock = co(function* removeBlock(entry) {
|
||||
*/
|
||||
|
||||
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;
|
||||
|
||||
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,
|
||||
// the automatic rescan will get the database
|
||||
// back in the correct state.
|
||||
try {
|
||||
wallets = yield this.mapWallets(tx);
|
||||
} catch (e) {
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
wallets = yield this.mapWallets(tx);
|
||||
|
||||
if (!wallets) {
|
||||
unlock();
|
||||
if (!wallets)
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger.info(
|
||||
'Incoming transaction for %d wallets (%s).',
|
||||
@ -1398,16 +1370,10 @@ WalletDB.prototype.addTX = co(function* addTX(tx, force) {
|
||||
|
||||
info.id = wallet.id;
|
||||
|
||||
try {
|
||||
yield wallet.tx.add(tx, info);
|
||||
yield wallet.handleTX(info);
|
||||
} catch (e) {
|
||||
unlock();
|
||||
throw e;
|
||||
}
|
||||
yield wallet.tx.add(tx, info);
|
||||
yield wallet.handleTX(info);
|
||||
}
|
||||
|
||||
unlock();
|
||||
return wallets;
|
||||
});
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user