wait for flush.
This commit is contained in:
parent
0139e1340c
commit
d22dc02e8f
@ -44,6 +44,9 @@ function BlockDB(options) {
|
|||||||
tx: new bcoin.lru(32 * 1024 * 1024)
|
tx: new bcoin.lru(32 * 1024 * 1024)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (+process.env.BCOIN_FRESH === 1 && bcoin.cp)
|
||||||
|
bcoin.cp.execFileSync('rm', ['-rf', this.file], { stdio: 'ignore' });
|
||||||
|
|
||||||
this.index = new levelup(this.file, {
|
this.index = new levelup(this.file, {
|
||||||
keyEncoding: 'ascii',
|
keyEncoding: 'ascii',
|
||||||
valueEncoding: 'binary',
|
valueEncoding: 'binary',
|
||||||
@ -895,6 +898,50 @@ BlockDB.prototype.hasCoin = function hasCoin(hash, index, callback) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// For BIP30
|
||||||
|
// https://bitcointalk.org/index.php?topic=67738.0
|
||||||
|
BlockDB.prototype.hasUnspentTX = function hasUnspentTX(hash, callback) {
|
||||||
|
var self = this;
|
||||||
|
this.getTX(hash, function(err, tx) {
|
||||||
|
var called, hash, pending, spent;
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
|
||||||
|
if (!tx)
|
||||||
|
return callback(null, false);
|
||||||
|
|
||||||
|
hash = tx.hash('hex');
|
||||||
|
pending = tx.outputs.length;
|
||||||
|
spent = 0;
|
||||||
|
|
||||||
|
if (!pending)
|
||||||
|
return callback(null, false);
|
||||||
|
|
||||||
|
function done(err) {
|
||||||
|
if (called)
|
||||||
|
return;
|
||||||
|
called = true;
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
return callback(null, spent < tx.outputs.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
tx.outputs.forEach(function(output, i) {
|
||||||
|
self.isSpent(hash, i, function(err, result) {
|
||||||
|
if (err)
|
||||||
|
return done(err);
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
spent++;
|
||||||
|
|
||||||
|
if (!--pending)
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
BlockDB.prototype.hasTX = function hasTX(hash, callback) {
|
BlockDB.prototype.hasTX = function hasTX(hash, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var id = 't/t/' + hash;
|
var id = 't/t/' + hash;
|
||||||
|
|||||||
@ -1128,8 +1128,10 @@ Chain.prototype.add = function add(initial, peer, callback) {
|
|||||||
|
|
||||||
// Start resolving the queue
|
// Start resolving the queue
|
||||||
// (I love asynchronous IO).
|
// (I love asynchronous IO).
|
||||||
if (self.pending.length === 0)
|
if (self.pending.length === 0) {
|
||||||
|
self.emit('flush');
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
item = self.pending.shift();
|
item = self.pending.shift();
|
||||||
delete self.pendingBlocks[item[0].hash('hex')];
|
delete self.pendingBlocks[item[0].hash('hex')];
|
||||||
|
|||||||
@ -77,6 +77,13 @@ Mempool.prototype.removeBlock = function removeBlock(block) {
|
|||||||
|
|
||||||
self.removeTX(mtx);
|
self.removeTX(mtx);
|
||||||
});
|
});
|
||||||
|
// Add transaction back into mempool
|
||||||
|
tx = tx.clone();
|
||||||
|
tx.ps = utils.now();
|
||||||
|
tx.ts = 0;
|
||||||
|
tx.block = null;
|
||||||
|
tx.network = true;
|
||||||
|
self.addTX(tx);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -201,6 +208,8 @@ Mempool.prototype.addTX = function addTX(tx, peer, callback) {
|
|||||||
if (tx.isCoinbase())
|
if (tx.isCoinbase())
|
||||||
return callback(new Error('What?'));
|
return callback(new Error('What?'));
|
||||||
|
|
||||||
|
assert(tx.ts === 0);
|
||||||
|
|
||||||
this._lockTX(tx);
|
this._lockTX(tx);
|
||||||
|
|
||||||
this.blockdb.fillCoin(tx, function(err) {
|
this.blockdb.fillCoin(tx, function(err) {
|
||||||
@ -217,7 +226,6 @@ Mempool.prototype.addTX = function addTX(tx, peer, callback) {
|
|||||||
data: tx.hash(),
|
data: tx.hash(),
|
||||||
reason: 'no-prevout'
|
reason: 'no-prevout'
|
||||||
});
|
});
|
||||||
pool.setMisbehavior(peer, 100);
|
|
||||||
return callback(new Error('Previous outputs not found.'));
|
return callback(new Error('Previous outputs not found.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -316,8 +324,12 @@ Mempool.prototype.addTX = function addTX(tx, peer, callback) {
|
|||||||
priority = tx.getPriority();
|
priority = tx.getPriority();
|
||||||
|
|
||||||
tx.inputs.forEach(function(input) {
|
tx.inputs.forEach(function(input) {
|
||||||
|
var type = input.getType();
|
||||||
var address = input.getAddress();
|
var address = input.getAddress();
|
||||||
|
|
||||||
|
if (type === 'pubkey' || type === 'multisig')
|
||||||
|
address = null;
|
||||||
|
|
||||||
if (!address)
|
if (!address)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -328,8 +340,12 @@ Mempool.prototype.addTX = function addTX(tx, peer, callback) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
tx.outputs.forEach(function(output) {
|
tx.outputs.forEach(function(output) {
|
||||||
|
var type = output.getType();
|
||||||
var address = output.getAddress();
|
var address = output.getAddress();
|
||||||
|
|
||||||
|
if (type === 'pubkey' || type === 'multisig')
|
||||||
|
address = null;
|
||||||
|
|
||||||
if (!address)
|
if (!address)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -401,8 +417,12 @@ Mempool.prototype.removeTX = function removeTX(hash, callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tx.inputs.forEach(function(input) {
|
tx.inputs.forEach(function(input) {
|
||||||
|
var type = input.getType();
|
||||||
var address = input.getAddress();
|
var address = input.getAddress();
|
||||||
|
|
||||||
|
if (type === 'pubkey' || type === 'multisig')
|
||||||
|
address = null;
|
||||||
|
|
||||||
if (!address)
|
if (!address)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -414,8 +434,12 @@ Mempool.prototype.removeTX = function removeTX(hash, callback) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
tx.outputs.forEach(function(output) {
|
tx.outputs.forEach(function(output) {
|
||||||
|
var type = output.getType();
|
||||||
var address = output.getAddress();
|
var address = output.getAddress();
|
||||||
|
|
||||||
|
if (type === 'pubkey' || type === 'multisig')
|
||||||
|
address = null;
|
||||||
|
|
||||||
if (!address)
|
if (!address)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
@ -182,6 +182,7 @@ Peer.prototype._init = function init() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Peer.prototype.createSocket = function createSocket(port, host) {
|
Peer.prototype.createSocket = function createSocket(port, host) {
|
||||||
|
var self = this;
|
||||||
var net, socket;
|
var net, socket;
|
||||||
|
|
||||||
assert(port != null);
|
assert(port != null);
|
||||||
@ -205,7 +206,7 @@ Peer.prototype.createSocket = function createSocket(port, host) {
|
|||||||
socket.on('connect', function() {
|
socket.on('connect', function() {
|
||||||
utils.debug(
|
utils.debug(
|
||||||
'Connected to %s:%d (priority=%s)',
|
'Connected to %s:%d (priority=%s)',
|
||||||
host, port, this.priority);
|
host, port, self.priority);
|
||||||
});
|
});
|
||||||
|
|
||||||
return socket;
|
return socket;
|
||||||
|
|||||||
@ -233,10 +233,7 @@ Pool.prototype._init = function _init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Resolve orphan chain.
|
// Resolve orphan chain.
|
||||||
self.peers.load.loadBlocks(
|
self.loadOrphan(self.peers.load, null, data.hash);
|
||||||
self.chain.getLocator(),
|
|
||||||
self.chain.getOrphanRoot(data.hash)
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
// Increase banscore by 10 if we're using getheaders.
|
// Increase banscore by 10 if we're using getheaders.
|
||||||
if (!self.options.multiplePeers)
|
if (!self.options.multiplePeers)
|
||||||
@ -258,6 +255,31 @@ Pool.prototype._init = function _init() {
|
|||||||
this.startServer();
|
this.startServer();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Pool.prototype.loadBlocks = function loadBlocks(peer, top, stop) {
|
||||||
|
var self = this;
|
||||||
|
this._onFlush(function() {
|
||||||
|
peer.loadBlocks(self.chain.getLocator(top), stop);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Pool.prototype.loadOrphan = function loadOrphan(peer, top, orphan) {
|
||||||
|
var self = this;
|
||||||
|
assert(orphan);
|
||||||
|
this._onFlush(function() {
|
||||||
|
peer.loadBlocks(
|
||||||
|
self.chain.getLocator(top),
|
||||||
|
self.chain.getOrphanRoot(orphan)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Pool.prototype.loadHeaders = function loadHeaders(peer, top, stop) {
|
||||||
|
var self = this;
|
||||||
|
this._onFlush(function() {
|
||||||
|
peer.loadHeaders(self.chain.getLocator(top), stop);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
Pool.prototype.startServer = function startServer() {
|
Pool.prototype.startServer = function startServer() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
@ -529,13 +551,14 @@ Pool.prototype._handleHeaders = function _handleHeaders(headers, peer) {
|
|||||||
// simply tries to find the latest block in
|
// simply tries to find the latest block in
|
||||||
// the peer's chain.
|
// the peer's chain.
|
||||||
if (last && headers.length === 2000)
|
if (last && headers.length === 2000)
|
||||||
peer.loadHeaders(this.chain.getLocator(last), null);
|
this.loadHeaders(peer, last, null);
|
||||||
|
|
||||||
// Reset interval to avoid calling getheaders unnecessarily
|
// Reset interval to avoid calling getheaders unnecessarily
|
||||||
this._startInterval();
|
this._startInterval();
|
||||||
};
|
};
|
||||||
|
|
||||||
Pool.prototype._handleBlocks = function _handleBlocks(hashes, peer) {
|
Pool.prototype._handleBlocks = function _handleBlocks(hashes, peer) {
|
||||||
|
var self = this;
|
||||||
var i, hash;
|
var i, hash;
|
||||||
|
|
||||||
assert(!this.options.headers);
|
assert(!this.options.headers);
|
||||||
@ -555,37 +578,36 @@ Pool.prototype._handleBlocks = function _handleBlocks(hashes, peer) {
|
|||||||
|
|
||||||
this.emit('blocks', hashes);
|
this.emit('blocks', hashes);
|
||||||
|
|
||||||
for (i = 0; i < hashes.length; i++) {
|
this._onFlush(function() {
|
||||||
hash = hashes[i];
|
for (i = 0; i < hashes.length; i++) {
|
||||||
|
hash = hashes[i];
|
||||||
|
|
||||||
if (this.chain.hasOrphan(hash)) {
|
if (self.chain.hasOrphan(hash)) {
|
||||||
utils.debug('Peer sent a hash that is already a known orphan.');
|
utils.debug('Peer sent a hash that is already a known orphan.');
|
||||||
|
|
||||||
// Make sure the peer doesn't send us
|
// Make sure the peer doesn't send us
|
||||||
// more than 200 orphans every 3 minutes.
|
// more than 200 orphans every 3 minutes.
|
||||||
if (this.isOrphaning(peer)) {
|
if (self.isOrphaning(peer)) {
|
||||||
utils.debug('Peer is orphaning (%s)', peer.host);
|
utils.debug('Peer is orphaning (%s)', peer.host);
|
||||||
this.setMisbehavior(peer, 100);
|
self.setMisbehavior(peer, 100);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve orphan chain.
|
||||||
|
self.loadOrphan(peer, null, hash);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve orphan chain.
|
// Request block if we don't have it or if
|
||||||
peer.loadBlocks(
|
// this is the last hash: this is done as
|
||||||
this.chain.getLocator(),
|
// a failsafe because we _need_ to request
|
||||||
this.chain.getOrphanRoot(hash)
|
// the hashContinue no matter what.
|
||||||
);
|
if (!self.chain.has(hash) || i === hashes.length - 1) {
|
||||||
continue;
|
self._request(peer, self.block.type, hash);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
// Request block if we don't have it or if
|
|
||||||
// this is the last hash: this is done as
|
|
||||||
// a failsafe because we _need_ to request
|
|
||||||
// the hashContinue no matter what.
|
|
||||||
if (!this.chain.has(hash) || i === hashes.length - 1) {
|
|
||||||
this._request(peer, this.block.type, hash);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset interval to avoid calling getblocks unnecessarily
|
// Reset interval to avoid calling getblocks unnecessarily
|
||||||
this._startInterval();
|
this._startInterval();
|
||||||
@ -594,6 +616,13 @@ Pool.prototype._handleBlocks = function _handleBlocks(hashes, peer) {
|
|||||||
this._startTimer();
|
this._startTimer();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Pool.prototype._onFlush = function _onFlush(callback) {
|
||||||
|
if (this.chain.pending.length === 0)
|
||||||
|
return callback();
|
||||||
|
|
||||||
|
this.chain.once('flush', callback);
|
||||||
|
};
|
||||||
|
|
||||||
Pool.prototype._handleInv = function _handleInv(hashes, peer) {
|
Pool.prototype._handleInv = function _handleInv(hashes, peer) {
|
||||||
var i, hash;
|
var i, hash;
|
||||||
|
|
||||||
@ -605,7 +634,7 @@ Pool.prototype._handleInv = function _handleInv(hashes, peer) {
|
|||||||
hash = utils.toHex(hashes[i]);
|
hash = utils.toHex(hashes[i]);
|
||||||
if (!this.chain.has(hash)) {
|
if (!this.chain.has(hash)) {
|
||||||
if (this.options.headers)
|
if (this.options.headers)
|
||||||
this.peers.load.loadHeaders(this.chain.getLocator(), hash);
|
this.loadHeaders(this.peers.load, null, hash);
|
||||||
else
|
else
|
||||||
this._request(peer, this.block.type, hash);
|
this._request(peer, this.block.type, hash);
|
||||||
}
|
}
|
||||||
@ -635,6 +664,7 @@ Pool.prototype._handleBlock = function _handleBlock(block, peer, callback) {
|
|||||||
utils.debug(
|
utils.debug(
|
||||||
'Recieved unrequested block: %s (%s)',
|
'Recieved unrequested block: %s (%s)',
|
||||||
block.rhash, peer.host);
|
block.rhash, peer.host);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._prehandleBlock(block, peer, function(err) {
|
this._prehandleBlock(block, peer, function(err) {
|
||||||
@ -687,9 +717,9 @@ Pool.prototype._load = function _load() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.options.headers)
|
if (this.options.headers)
|
||||||
this.peers.load.loadHeaders(this.chain.getLocator(), null);
|
this.loadHeaders(this.peers.load, null, null);
|
||||||
else
|
else
|
||||||
this.peers.load.loadBlocks(this.chain.getLocator(), null);
|
this.loadBlocks(this.peers.load, null, null);
|
||||||
};
|
};
|
||||||
|
|
||||||
Pool.prototype.loadMempool = function loadMempool() {
|
Pool.prototype.loadMempool = function loadMempool() {
|
||||||
@ -1435,13 +1465,6 @@ Pool.prototype._request = function _request(peer, type, hash, options, cb) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Block should be not in chain, or be requested
|
|
||||||
// Do not use with headers-first
|
|
||||||
// if (!options.force && (type === 'block' || type === 'filtered')) {
|
|
||||||
// if (this.chain.has(hash))
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
item = new LoadRequest(this, peer, type, hash, cb);
|
item = new LoadRequest(this, peer, type, hash, cb);
|
||||||
|
|
||||||
if (item.type === 'tx') {
|
if (item.type === 'tx') {
|
||||||
@ -1467,15 +1490,17 @@ Pool.prototype._request = function _request(peer, type, hash, options, cb) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (peer._blockQueue.length === 0) {
|
if (peer._blockQueue.length === 0) {
|
||||||
utils.nextTick(function() {
|
this._onFlush(function() {
|
||||||
utils.debug(
|
utils.nextTick(function() {
|
||||||
'Requesting %d/%d blocks from %s with getdata',
|
utils.debug(
|
||||||
peer._blockQueue.length,
|
'Requesting %d/%d blocks from %s with getdata',
|
||||||
self.request.activeBlocks,
|
peer._blockQueue.length,
|
||||||
peer.host);
|
self.request.activeBlocks,
|
||||||
|
peer.host);
|
||||||
|
|
||||||
peer.getData(peer._blockQueue);
|
peer.getData(peer._blockQueue);
|
||||||
peer._blockQueue.length = 0;
|
peer._blockQueue.length = 0;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -309,6 +309,8 @@ script.checksig = function checksig(msg, sig, key) {
|
|||||||
try {
|
try {
|
||||||
return bcoin.ecdsa.verify(msg, sig, key);
|
return bcoin.ecdsa.verify(msg, sig, key);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
utils.debug('Elliptic threw during verification:');
|
||||||
|
utils.debug(e.stack + '');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user