reject packets. pool refactor.
This commit is contained in:
parent
5023c7efe4
commit
6d5e277629
@ -65,22 +65,29 @@ AbstractBlock.prototype.getSize = function getSize() {
|
||||
return this._size || this.render().length;
|
||||
};
|
||||
|
||||
AbstractBlock.prototype.verify = function verify() {
|
||||
AbstractBlock.prototype.verify = function verify(ret) {
|
||||
if (this.valid == null)
|
||||
this.valid = this._verify();
|
||||
this.valid = this._verify(ret);
|
||||
return this.valid;
|
||||
};
|
||||
|
||||
AbstractBlock.prototype.verifyHeaders = function verifyHeaders() {
|
||||
AbstractBlock.prototype.verifyHeaders = function verifyHeaders(ret) {
|
||||
if (!ret)
|
||||
ret = {};
|
||||
|
||||
// Check proof of work
|
||||
if (!utils.testTarget(this.bits, this.hash())) {
|
||||
utils.debug('Block failed POW test: %s', this.rhash);
|
||||
ret.reason = 'high-hash';
|
||||
ret.score = 50;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check timestamp against now + 2 hours
|
||||
if (this.ts > utils.now() + 2 * 60 * 60) {
|
||||
utils.debug('Block timestamp is too high: %s', this.rhash);
|
||||
ret.reason = 'time-too-new';
|
||||
ret.score = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -168,23 +168,30 @@ Block.prototype.__defineGetter__('commitmentHash', function() {
|
||||
return this._commitmentHash;
|
||||
});
|
||||
|
||||
Block.prototype._verify = function _verify() {
|
||||
Block.prototype._verify = function _verify(ret) {
|
||||
var uniq = {};
|
||||
var i, tx, hash;
|
||||
|
||||
if (!this.verifyHeaders())
|
||||
if (!ret)
|
||||
ret = {};
|
||||
|
||||
if (!this.verifyHeaders(ret))
|
||||
return false;
|
||||
|
||||
// Size can't be bigger than MAX_BLOCK_SIZE
|
||||
if (this.txs.length > constants.block.maxSize
|
||||
|| this.getVirtualSize() > constants.block.maxSize) {
|
||||
utils.debug('Block is too large: %s', this.rhash);
|
||||
ret.reason = 'bad-blk-length';
|
||||
ret.score = 100;
|
||||
return false;
|
||||
}
|
||||
|
||||
// First TX must be a coinbase
|
||||
if (!this.txs.length || !this.txs[0].isCoinbase()) {
|
||||
utils.debug('Block has no coinbase: %s', this.rhash);
|
||||
ret.reason = 'bad-cb-missing';
|
||||
ret.score = 100;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -195,6 +202,8 @@ Block.prototype._verify = function _verify() {
|
||||
// The rest of the txs must not be coinbases
|
||||
if (i > 0 && tx.isCoinbase()) {
|
||||
utils.debug('Block more than one coinbase: %s', this.rhash);
|
||||
ret.reason = 'bad-cb-multiple';
|
||||
ret.score = 100;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -202,6 +211,8 @@ Block.prototype._verify = function _verify() {
|
||||
hash = tx.hash('hex');
|
||||
if (uniq[hash]) {
|
||||
utils.debug('Block has duplicate txids: %s', this.rhash);
|
||||
ret.reason = 'bad-txns-duplicate';
|
||||
ret.score = 100;
|
||||
return false;
|
||||
}
|
||||
uniq[hash] = true;
|
||||
@ -210,6 +221,8 @@ Block.prototype._verify = function _verify() {
|
||||
// Check merkle root
|
||||
if (this.getMerkleRoot() !== this.merkleRoot) {
|
||||
utils.debug('Block failed merkleroot test: %s', this.rhash);
|
||||
ret.reason = 'bad-txnmrkleroot';
|
||||
ret.score = 100;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -331,24 +331,24 @@ Chain.prototype._preload = function _preload(callback) {
|
||||
});
|
||||
};
|
||||
|
||||
Chain.prototype._verifyContext = function _verifyContext(block, prev, callback) {
|
||||
Chain.prototype._verifyContext = function _verifyContext(block, prev, peer, callback) {
|
||||
var self = this;
|
||||
|
||||
this._verify(block, prev, function(err, flags) {
|
||||
this._verify(block, prev, peer, function(err, flags) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (flags === false)
|
||||
return callback(null, false);
|
||||
|
||||
self._checkDuplicates(block, prev, function(err, result) {
|
||||
self._checkDuplicates(block, prev, peer, function(err, result) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (!result)
|
||||
return callback(null, false);
|
||||
|
||||
self._checkInputs(block, prev, flags, function(err, result) {
|
||||
self._checkInputs(block, prev, flags, peer, function(err, result) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
@ -361,19 +361,22 @@ Chain.prototype._verifyContext = function _verifyContext(block, prev, callback)
|
||||
});
|
||||
};
|
||||
|
||||
Chain.prototype._verify = function _verify(block, prev, callback) {
|
||||
Chain.prototype._verify = function _verify(block, prev, peer, callback) {
|
||||
var self = this;
|
||||
var flags = constants.flags.MANDATORY_VERIFY_FLAGS;
|
||||
var height, ts, i, tx, coinbaseHeight;
|
||||
var medianTime, locktimeMedian, segwit;
|
||||
var ret = {};
|
||||
|
||||
function done(err, result) {
|
||||
prev.free();
|
||||
callback(err, result);
|
||||
}
|
||||
|
||||
if (!block.verify())
|
||||
if (!block.verify(ret)) {
|
||||
self.emit('verify-error', block, ret.reason, ret.score, peer);
|
||||
return done(null, false);
|
||||
}
|
||||
|
||||
// Skip the genesis block
|
||||
if (block.isGenesis())
|
||||
@ -395,11 +398,13 @@ Chain.prototype._verify = function _verify(block, prev, callback) {
|
||||
// Ensure the timestamp is correct
|
||||
if (block.ts <= medianTime) {
|
||||
utils.debug('Block time is lower than median: %s', block.rhash);
|
||||
self.emit('verify-error', block, 'time-too-old', 0, peer);
|
||||
return done(null, false);
|
||||
}
|
||||
|
||||
if (block.bits !== self.getTarget(prev, block)) {
|
||||
utils.debug('Block is using wrong target: %s', block.rhash);
|
||||
self.emit('verify-error', block, 'bad-diffbits', 100, peer);
|
||||
return done(null, false);
|
||||
}
|
||||
|
||||
@ -418,6 +423,7 @@ Chain.prototype._verify = function _verify(block, prev, callback) {
|
||||
// once the majority of blocks are using it.
|
||||
if (block.version < 2 && prev.isOutdated(2)) {
|
||||
utils.debug('Block is outdated (v2): %s', block.rhash);
|
||||
self.emit('verify-error', block, 'bad-version', 0, peer);
|
||||
return done(null, false);
|
||||
}
|
||||
|
||||
@ -425,6 +431,7 @@ Chain.prototype._verify = function _verify(block, prev, callback) {
|
||||
// once the majority of blocks are using it.
|
||||
if (block.version < 3 && prev.isOutdated(3)) {
|
||||
utils.debug('Block is outdated (v3): %s', block.rhash);
|
||||
self.emit('verify-error', block, 'bad-version', 0, peer);
|
||||
return done(null, false);
|
||||
}
|
||||
|
||||
@ -432,6 +439,7 @@ Chain.prototype._verify = function _verify(block, prev, callback) {
|
||||
// once the majority of blocks are using it.
|
||||
if (block.version < 4 && prev.isOutdated(4)) {
|
||||
utils.debug('Block is outdated (v4): %s', block.rhash);
|
||||
self.emit('verify-error', block, 'bad-version', 0, peer);
|
||||
return done(null, false);
|
||||
}
|
||||
|
||||
@ -440,6 +448,7 @@ Chain.prototype._verify = function _verify(block, prev, callback) {
|
||||
if (network.segwitHeight !== -1 && height >= network.segwitHeight) {
|
||||
if (block.version < 5 && prev.isOutdated(5)) {
|
||||
utils.debug('Block is outdated (v5): %s', block.rhash);
|
||||
self.emit('verify-error', block, 'bad-version', 0, peer);
|
||||
return done(null, false);
|
||||
}
|
||||
}
|
||||
@ -481,6 +490,7 @@ Chain.prototype._verify = function _verify(block, prev, callback) {
|
||||
if (coinbaseHeight) {
|
||||
if (block.getCoinbaseHeight() !== height) {
|
||||
utils.debug('Block has bad coinbase height: %s', block.rhash);
|
||||
self.emit('verify-error', block, 'bad-cb-height', 100, peer);
|
||||
return done(null, false);
|
||||
}
|
||||
}
|
||||
@ -488,11 +498,13 @@ Chain.prototype._verify = function _verify(block, prev, callback) {
|
||||
if (block.version >= 5 && segwit) {
|
||||
if (block.commitmentHash !== block.getCommitmentHash()) {
|
||||
utils.debug('Block failed witnessroot test: %s', block.rhash);
|
||||
self.emit('verify-error', block, 'bad-blk-wit-length', 100, peer);
|
||||
return done(null, false);
|
||||
}
|
||||
} else {
|
||||
if (block.hasWitness()) {
|
||||
utils.debug('Unexpected witness data found: %s', block.rhash);
|
||||
self.emit('verify-error', block, 'unexpected-witness', 100, peer);
|
||||
return done(null, false);
|
||||
}
|
||||
}
|
||||
@ -508,6 +520,7 @@ Chain.prototype._verify = function _verify(block, prev, callback) {
|
||||
// regards to nSequence and nLockTime.
|
||||
if (!tx.isFinal(height, ts)) {
|
||||
utils.debug('TX is not final: %s (%s)', block.rhash, i);
|
||||
self.emit('verify-error', block, 'bad-txns-nonfinal', 10, peer);
|
||||
return done(null, false);
|
||||
}
|
||||
}
|
||||
@ -516,7 +529,7 @@ Chain.prototype._verify = function _verify(block, prev, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
Chain.prototype._checkDuplicates = function _checkDuplicates(block, prev, callback) {
|
||||
Chain.prototype._checkDuplicates = function _checkDuplicates(block, prev, peer, callback) {
|
||||
var self = this;
|
||||
var height = prev.height + 1;
|
||||
|
||||
@ -540,8 +553,10 @@ Chain.prototype._checkDuplicates = function _checkDuplicates(block, prev, callba
|
||||
// and extraNonce.
|
||||
if (result) {
|
||||
utils.debug('Block is overwriting txids: %s', block.rhash);
|
||||
if (!(network.type === 'main' && (height === 91842 || height === 91880)))
|
||||
if (!(network.type === 'main' && (height === 91842 || height === 91880))) {
|
||||
self.emit('verify-error', block, 'bad-txns-BIP30', 100, peer);
|
||||
return next(null, false);
|
||||
}
|
||||
}
|
||||
|
||||
next(null, true);
|
||||
@ -549,7 +564,7 @@ Chain.prototype._checkDuplicates = function _checkDuplicates(block, prev, callba
|
||||
}, callback);
|
||||
};
|
||||
|
||||
Chain.prototype._checkInputs = function _checkInputs(block, prev, flags, callback) {
|
||||
Chain.prototype._checkInputs = function _checkInputs(block, prev, flags, peer, callback) {
|
||||
var height = prev.height + 1;
|
||||
var scriptCheck = true;
|
||||
|
||||
@ -588,6 +603,7 @@ Chain.prototype._checkInputs = function _checkInputs(block, prev, flags, callbac
|
||||
|
||||
if (sigops > constants.script.maxBlockSigops) {
|
||||
utils.debug('Block has too many sigops: %s', block.rhash);
|
||||
self.emit('verify-error', block, 'bad-blk-sigops', 100, peer);
|
||||
return callback(null, false);
|
||||
}
|
||||
|
||||
@ -610,6 +626,7 @@ Chain.prototype._checkInputs = function _checkInputs(block, prev, flags, callbac
|
||||
utils.revHex(input.prevout.hash) + '/' + input.prevout.index);
|
||||
if (height < network.checkpoints.lastHeight)
|
||||
throw new Error('BUG: Spent inputs in historical data!');
|
||||
self.emit('verify-error', block, 'bad-txns-inputs-missingorspent', 100, peer);
|
||||
return callback(null, false);
|
||||
}
|
||||
|
||||
@ -661,8 +678,10 @@ Chain.prototype._checkReward = function _checkReward(block) {
|
||||
for (i = 1; i < block.txs.length; i++)
|
||||
actual.iadd(block.txs[i].getFee());
|
||||
|
||||
if (claimed.cmp(actual) > 0)
|
||||
if (claimed.cmp(actual) > 0) {
|
||||
self.emit('verify-error', block, 'bad-cb-amount', 100, peer);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
@ -910,6 +929,7 @@ Chain.prototype.onFlush = function onFlush(callback) {
|
||||
Chain.prototype.add = function add(initial, peer, callback, force) {
|
||||
var self = this;
|
||||
var total = 0;
|
||||
var ret = {};
|
||||
|
||||
assert(this.loaded);
|
||||
|
||||
@ -951,6 +971,7 @@ Chain.prototype.add = function add(initial, peer, callback, force) {
|
||||
chain: !!self.invalid[prevHash]
|
||||
}, peer);
|
||||
self.invalid[hash] = true;
|
||||
self.emit('verify-error', block, 'duplicate', 0, peer);
|
||||
return done();
|
||||
}
|
||||
|
||||
@ -958,7 +979,7 @@ Chain.prototype.add = function add(initial, peer, callback, force) {
|
||||
// This is only necessary for new
|
||||
// blocks coming in, not the resolving
|
||||
// orphans.
|
||||
if (block === initial && !block.verify()) {
|
||||
if (block === initial && !block.verify(ret)) {
|
||||
self.invalid[hash] = true;
|
||||
self.emit('invalid', block, {
|
||||
height: height,
|
||||
@ -966,6 +987,7 @@ Chain.prototype.add = function add(initial, peer, callback, force) {
|
||||
seen: false,
|
||||
chain: false
|
||||
}, peer);
|
||||
self.emit('verify-error', block, ret.reason, ret.score, peer);
|
||||
return done();
|
||||
}
|
||||
|
||||
@ -999,6 +1021,9 @@ Chain.prototype.add = function add(initial, peer, callback, force) {
|
||||
seen: true
|
||||
}, peer);
|
||||
|
||||
if (self.options.headers)
|
||||
self.emit('verify-error', block, 'bad-prevblk', 0, peer);
|
||||
|
||||
return done();
|
||||
}
|
||||
|
||||
@ -1021,6 +1046,8 @@ Chain.prototype.add = function add(initial, peer, callback, force) {
|
||||
hash: hash,
|
||||
seen: false
|
||||
}, peer);
|
||||
if (self.options.headers)
|
||||
self.emit('verify-error', block, 'bad-prevblk', 0, peer);
|
||||
return done();
|
||||
}
|
||||
|
||||
@ -1050,6 +1077,8 @@ Chain.prototype.add = function add(initial, peer, callback, force) {
|
||||
checkpoint: true
|
||||
}, peer);
|
||||
|
||||
self.emit('verify-error', block, 'checkpoint mismatch', 100, peer);
|
||||
|
||||
return done();
|
||||
}
|
||||
}
|
||||
@ -1087,7 +1116,7 @@ Chain.prototype.add = function add(initial, peer, callback, force) {
|
||||
// Do "contextual" verification on our block
|
||||
// now that we're certain its previous
|
||||
// block is in the chain.
|
||||
self._verifyContext(block, prev, function(err, verified) {
|
||||
self._verifyContext(block, prev, peer, function(err, verified) {
|
||||
var entry;
|
||||
|
||||
// Couldn't verify block.
|
||||
|
||||
@ -30,8 +30,8 @@ function CompactBlock(data) {
|
||||
|
||||
utils.inherits(CompactBlock, bcoin.abstractblock);
|
||||
|
||||
CompactBlock.prototype._verify = function _verify() {
|
||||
return this.verifyHeaders();
|
||||
CompactBlock.prototype._verify = function _verify(ret) {
|
||||
return this.verifyHeaders(ret);
|
||||
};
|
||||
|
||||
CompactBlock.prototype.getCoinbaseHeight = function getCoinbaseHeight() {
|
||||
|
||||
@ -54,6 +54,7 @@ function Mempool(node, options) {
|
||||
this.pendingTX = {};
|
||||
this.pendingSize = 0;
|
||||
this.pendingLimit = 20 << 20;
|
||||
this.locker = new bcoin.locker(this, this.add, this.pendingLimit);
|
||||
|
||||
this.freeCount = 0;
|
||||
this.lastTime = 0;
|
||||
@ -74,79 +75,11 @@ Mempool.flags = constants.flags.STANDARD_VERIFY_FLAGS;
|
||||
Mempool.mandatory = constants.flags.MANDATORY_VERIFY_FLAGS;
|
||||
|
||||
Mempool.prototype._lock = function _lock(func, args, force) {
|
||||
var self = this;
|
||||
var tx, called;
|
||||
|
||||
if (force) {
|
||||
assert(this.busy);
|
||||
return function unlock() {
|
||||
assert(!called);
|
||||
called = true;
|
||||
};
|
||||
}
|
||||
|
||||
if (this.busy) {
|
||||
if (func === Mempool.prototype.add) {
|
||||
tx = args[0];
|
||||
this.pending.push(tx);
|
||||
this.pendingTX[tx.hash('hex')] = true;
|
||||
this.pendingSize += tx.getSize();
|
||||
if (this.pendingSize > this.pendingLimit) {
|
||||
this.purgePending();
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.jobs.push([func, args]);
|
||||
return;
|
||||
}
|
||||
|
||||
this.busy = true;
|
||||
|
||||
return function unlock() {
|
||||
var item, tx;
|
||||
|
||||
assert(!called);
|
||||
called = true;
|
||||
|
||||
self.busy = false;
|
||||
|
||||
if (func === Mempool.prototype.add) {
|
||||
if (self.pending.length === 0)
|
||||
self.emit('flush');
|
||||
}
|
||||
|
||||
if (self.jobs.length === 0)
|
||||
return;
|
||||
|
||||
item = self.jobs.shift();
|
||||
|
||||
if (item[0] === Mempool.prototype.add) {
|
||||
tx = item[1][0];
|
||||
assert(tx === self.pending.shift());
|
||||
delete self.pendingTX[tx.hash('hex')];
|
||||
self.pendingSize -= tx.getSize();
|
||||
}
|
||||
|
||||
item[0].apply(self, item[1]);
|
||||
};
|
||||
return this.locker.lock(func, args, force);
|
||||
};
|
||||
|
||||
Mempool.prototype.purgePending = function purgePending() {
|
||||
var self = this;
|
||||
|
||||
utils.debug('Warning: %dmb of pending txs. Purging.',
|
||||
utils.mb(this.pendingSize));
|
||||
|
||||
this.pending.forEach(function(tx) {
|
||||
delete self.pendingTX[tx.hash('hex')];
|
||||
});
|
||||
|
||||
this.pending.length = 0;
|
||||
this.pendingSize = 0;
|
||||
|
||||
this.jobs = this.jobs.filter(function(item) {
|
||||
return item[0] !== Mempool.prototype.add;
|
||||
});
|
||||
return this.locker.purgePending();
|
||||
};
|
||||
|
||||
Mempool.prototype._init = function _init() {
|
||||
@ -488,10 +421,9 @@ Mempool.prototype.resolveOrphans = function resolveOrphans(tx, callback, force)
|
||||
var hashes = [];
|
||||
var resolved = [];
|
||||
var batch = this.db.batch();
|
||||
var p;
|
||||
|
||||
this.db.get('d/' + hash, function(err, buf) {
|
||||
var p;
|
||||
|
||||
if (err && err.type !== 'NotFoundError')
|
||||
return callback(err);
|
||||
|
||||
@ -600,6 +532,10 @@ Mempool.prototype.removeTX = function removeTX(hash, callback, force) {
|
||||
};
|
||||
|
||||
Mempool.prototype.checkTX = function checkTX(tx, peer) {
|
||||
return Mempool.checkTX(tx, peer);
|
||||
};
|
||||
|
||||
Mempool.checkTX = function checkTX(tx, peer) {
|
||||
var i, input, output, size;
|
||||
var total = new bn(0);
|
||||
var uniq = {};
|
||||
|
||||
@ -99,13 +99,18 @@ MerkleBlock.prototype._verifyPartial = function _verifyPartial() {
|
||||
return true;
|
||||
};
|
||||
|
||||
MerkleBlock.prototype._verify = function _verify() {
|
||||
if (!this.verifyHeaders())
|
||||
MerkleBlock.prototype._verify = function _verify(ret) {
|
||||
if (!ret)
|
||||
ret = {};
|
||||
|
||||
if (!this.verifyHeaders(ret))
|
||||
return false;
|
||||
|
||||
// Verify the partial merkle tree if we are a merkleblock.
|
||||
if (!this._verifyPartial()) {
|
||||
utils.debug('Block failed merkle test: %s', this.rhash);
|
||||
ret.reason = 'bad-txnmrklroot';
|
||||
ret.score = 100;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -186,43 +186,24 @@ Pool.prototype._init = function _init() {
|
||||
}
|
||||
});
|
||||
|
||||
this.chain.on('verify-error', function(block, reason, score, peer) {
|
||||
peer.sendReject(block, reason, score);
|
||||
});
|
||||
|
||||
this.chain.on('fork', function(block, data, peer) {
|
||||
self.emit('fork', data, peer);
|
||||
|
||||
if (!peer)
|
||||
return;
|
||||
|
||||
// If we failed a checkpoint, peer is misbehaving.
|
||||
if (data.checkpoint) {
|
||||
self.setMisbehavior(peer, 100);
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
this.chain.on('invalid', function(block, data, peer) {
|
||||
if (!peer)
|
||||
return;
|
||||
|
||||
self.setMisbehavior(peer, 100);
|
||||
self.emit('invalid', data, peer);
|
||||
});
|
||||
|
||||
this.chain.on('exists', function(block, data, peer) {
|
||||
if (!peer)
|
||||
return;
|
||||
|
||||
// self.setMisbehavior(peer, 1);
|
||||
self.emit('exists', data, peer);
|
||||
});
|
||||
|
||||
this.chain.on('orphan', function(block, data, peer) {
|
||||
if (!peer)
|
||||
return;
|
||||
|
||||
// Increase banscore by 10 if we're using getheaders.
|
||||
if (self.options.headers) {
|
||||
// self.setMisbehavior(peer, 10);
|
||||
return;
|
||||
}
|
||||
|
||||
self.emit('orphan', data, peer);
|
||||
// Resolve orphan chain
|
||||
self.resolveOrphan(self.peers.load, null, data.hash);
|
||||
});
|
||||
@ -520,9 +501,6 @@ Pool.prototype._addLoader = function _addLoader() {
|
||||
};
|
||||
|
||||
Pool.prototype.startSync = function startSync() {
|
||||
if (!this.loaded)
|
||||
return this.once('open', this.startSync.bind(this));
|
||||
|
||||
this.syncing = true;
|
||||
|
||||
this._startInterval();
|
||||
@ -567,7 +545,7 @@ Pool.prototype._handleHeaders = function _handleHeaders(headers, peer, callback)
|
||||
peer.host);
|
||||
|
||||
if (headers.length > 2000) {
|
||||
this.setMisbehavior(peer, 100);
|
||||
peer.setMisbehavior(100);
|
||||
return callback();
|
||||
}
|
||||
|
||||
@ -627,7 +605,7 @@ Pool.prototype._handleBlocks = function _handleBlocks(hashes, peer, callback) {
|
||||
// Normally this is 500, but with older
|
||||
// versions locator.GetDistanceBack() is called.
|
||||
// if (hashes.length > 500) {
|
||||
// this.setMisbehavior(peer, 100);
|
||||
// peer.setMisbehavior(100);
|
||||
// return;
|
||||
// }
|
||||
|
||||
@ -1140,29 +1118,19 @@ Pool.prototype.watch = function watch(id) {
|
||||
|
||||
if (id) {
|
||||
hid = utils.toHex(id);
|
||||
if (this.watchMap[hid])
|
||||
|
||||
if (this.watchMap[hid]) {
|
||||
this.watchMap[hid]++;
|
||||
else
|
||||
this.watchMap[hid] = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
this.watchMap[hid] = 1;
|
||||
|
||||
this.bloom.add(id);
|
||||
}
|
||||
|
||||
// Send it to peers
|
||||
if (this._pendingWatch)
|
||||
return;
|
||||
|
||||
this._pendingWatch = true;
|
||||
|
||||
utils.nextTick(function() {
|
||||
self._pendingWatch = false;
|
||||
|
||||
if (self.peers.load)
|
||||
self.peers.load.updateWatch();
|
||||
|
||||
for (i = 0; i < self.peers.regular.length; i++)
|
||||
self.peers.regular[i].updateWatch();
|
||||
});
|
||||
this.updateWatch();
|
||||
};
|
||||
|
||||
Pool.prototype.unwatch = function unwatch(id) {
|
||||
@ -1171,9 +1139,6 @@ Pool.prototype.unwatch = function unwatch(id) {
|
||||
|
||||
id = utils.toHex(id);
|
||||
|
||||
if (!this.bloom.test(id, 'hex'))
|
||||
return;
|
||||
|
||||
if (!this.watchMap[id] || --this.watchMap[id] !== 0)
|
||||
return;
|
||||
|
||||
@ -1182,10 +1147,16 @@ Pool.prototype.unwatch = function unwatch(id) {
|
||||
// Reset bloom filter
|
||||
this.bloom.reset();
|
||||
Object.keys(this.watchMap).forEach(function(id) {
|
||||
this.bloom.add(id, 'hex');
|
||||
this.bloom.add(id);
|
||||
}, this);
|
||||
|
||||
// Resend it to peers
|
||||
this.updateWatch();
|
||||
};
|
||||
|
||||
Pool.prototype.updateWatch = function updateWatch() {
|
||||
var self = this;
|
||||
|
||||
if (this._pendingWatch)
|
||||
return;
|
||||
|
||||
@ -1205,13 +1176,13 @@ Pool.prototype.unwatch = function unwatch(id) {
|
||||
// See "Filter matching algorithm":
|
||||
// https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki
|
||||
Pool.prototype.isWatched = function(tx, bloom) {
|
||||
var i, prev, input, output;
|
||||
var i, input, output;
|
||||
|
||||
if (!bloom)
|
||||
bloom = this.bloom;
|
||||
|
||||
function testScript(script) {
|
||||
return script.some(function(chunk) {
|
||||
function testScript(code) {
|
||||
return code.some(function(chunk) {
|
||||
if (!Buffer.isBuffer(chunk) || chunk.length === 0)
|
||||
return false;
|
||||
return bloom.test(chunk);
|
||||
@ -1227,7 +1198,7 @@ Pool.prototype.isWatched = function(tx, bloom) {
|
||||
for (i = 0; i < tx.outputs.length; i++) {
|
||||
output = tx.outputs[i];
|
||||
// Test the output script
|
||||
if (testScript(output.script))
|
||||
if (testScript(output.script.code))
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1235,23 +1206,23 @@ Pool.prototype.isWatched = function(tx, bloom) {
|
||||
// 4. Test data elements in input scripts
|
||||
for (i = 0; i < tx.inputs.length; i++) {
|
||||
input = tx.inputs[i];
|
||||
prev = input.prevout.hash;
|
||||
|
||||
if (typeof prev === 'string')
|
||||
prev = new Buffer(prev, 'hex');
|
||||
|
||||
// Test the prev_out tx hash
|
||||
if (bloom.test(prev))
|
||||
if (bloom.test(input.prevout.hash, 'hex'))
|
||||
return true;
|
||||
|
||||
// Test the prev_out script
|
||||
if (input.output) {
|
||||
if (testScript(input.output.script))
|
||||
if (testScript(input.output.script.code))
|
||||
return true;
|
||||
}
|
||||
|
||||
// Test the input script
|
||||
if (testScript(input.script))
|
||||
if (testScript(input.script.code))
|
||||
return true;
|
||||
|
||||
// Test the witness
|
||||
if (testScript(input.witness.items))
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1262,9 +1233,6 @@ Pool.prototype.isWatched = function(tx, bloom) {
|
||||
Pool.prototype.addWallet = function addWallet(wallet, callback) {
|
||||
var self = this;
|
||||
|
||||
if (!this.loaded)
|
||||
return this.once('open', this.addWallet.bind(this, wallet, callback));
|
||||
|
||||
callback = utils.asyncify(callback);
|
||||
|
||||
if (this.options.spv)
|
||||
@ -1295,13 +1263,13 @@ Pool.prototype.removeWallet = function removeWallet(wallet) {
|
||||
};
|
||||
|
||||
Pool.prototype.watchAddress = function watchAddress(address) {
|
||||
address = bcoin.address.parse(address);
|
||||
this.watch(address.hash);
|
||||
var hash = bcoin.address.parse(address).hash;
|
||||
this.watch(hash);
|
||||
};
|
||||
|
||||
Pool.prototype.unwatchAddress = function unwatchAddress(address) {
|
||||
address = bcoin.address.parse(address);
|
||||
this.unwatch(address.hash);
|
||||
var hash = bcoin.address.parse(address).hash;
|
||||
this.unwatch(hash);
|
||||
};
|
||||
|
||||
Pool.prototype.watchWallet = function watchWallet(wallet) {
|
||||
@ -1464,13 +1432,9 @@ Pool.prototype.getData = function getData(peer, type, hash, options, callback) {
|
||||
if (exists)
|
||||
return callback();
|
||||
|
||||
if (self.request.map[hash]) {
|
||||
// if (callback)
|
||||
// self.request.map[hash].callback.push(callback);
|
||||
if (self.request.map[hash])
|
||||
return callback();
|
||||
}
|
||||
|
||||
// item = new LoadRequest(self, peer, type, hash, callback);
|
||||
item = new LoadRequest(self, peer, type, hash);
|
||||
|
||||
if (options.noQueue)
|
||||
@ -1503,7 +1467,9 @@ Pool.prototype.getData = function getData(peer, type, hash, options, callback) {
|
||||
if (!options.force && type !== self.tx.type)
|
||||
return self.chain.has(hash, done);
|
||||
|
||||
return done(null, false);
|
||||
return utils.nextTick(function() {
|
||||
return done(null, false);
|
||||
});
|
||||
};
|
||||
|
||||
Pool.prototype.scheduleRequests = function scheduleRequests(peer) {
|
||||
@ -1587,15 +1553,15 @@ Pool.prototype.getBlock = function getBlock(hash, callback) {
|
||||
if (!this.peers.load)
|
||||
return setTimeout(this.getBlock.bind(this, hash, callback), 1000);
|
||||
|
||||
this.getData(this.peers.load, 'block', hash, { force: true }, function(block) {
|
||||
this.getData(this.peers.load, this.block.type, hash, { force: true }, function(block) {
|
||||
callback(null, block);
|
||||
});
|
||||
|
||||
this.scheduleRequests(this.peers.load);
|
||||
};
|
||||
|
||||
Pool.prototype.sendBlock = function sendBlock(block) {
|
||||
return this.broadcast(block);
|
||||
Pool.prototype.sendBlock = function sendBlock(block, callback) {
|
||||
return this.broadcast(block, callback);
|
||||
};
|
||||
|
||||
Pool.prototype.getTX = function getTX(hash, range, callback) {
|
||||
@ -1668,27 +1634,22 @@ Pool.prototype.getTX = function getTX(hash, range, callback) {
|
||||
})();
|
||||
};
|
||||
|
||||
Pool.prototype.sendTX = function sendTX(tx) {
|
||||
var flags = constants.flags.STANDARD_VERIFY_FLAGS;
|
||||
// This is to avoid getting banned by
|
||||
// bitcoind nodes. Possibly check
|
||||
// sigops. Call isStandard and/or
|
||||
// isStandardInputs as well.
|
||||
if (tx.hasPrevout()) {
|
||||
if (!tx.verify(null, true, flags)) {
|
||||
utils.debug(
|
||||
'Could not relay TX (%s). It does not verify.',
|
||||
tx.rhash);
|
||||
return;
|
||||
}
|
||||
}
|
||||
return this.broadcast(tx);
|
||||
Pool.prototype.sendTX = function sendTX(tx, callback) {
|
||||
callback = utils.asyncify(callback);
|
||||
|
||||
// Failsafe to avoid getting banned by bitcoind nodes.
|
||||
if (!bcoin.mempool.checkTX(tx))
|
||||
return callback(new Error('CheckTransaction failed.'));
|
||||
|
||||
return this.broadcast(tx, callback);
|
||||
};
|
||||
|
||||
Pool.prototype.broadcast = function broadcast(msg) {
|
||||
Pool.prototype.broadcast = function broadcast(msg, callback) {
|
||||
var self = this;
|
||||
var e = new EventEmitter();
|
||||
|
||||
callback = utils.once(callback);
|
||||
|
||||
var entry = {
|
||||
msg: msg,
|
||||
e: e,
|
||||
@ -1696,6 +1657,7 @@ Pool.prototype.broadcast = function broadcast(msg) {
|
||||
var i = self.inv.list.indexOf(entry);
|
||||
if (i !== -1)
|
||||
self.inv.list.splice(i, 1);
|
||||
callback(new Error('Timed out.'));
|
||||
}, this.inv.timeout)
|
||||
};
|
||||
|
||||
@ -1703,9 +1665,17 @@ Pool.prototype.broadcast = function broadcast(msg) {
|
||||
|
||||
this.peers.regular.forEach(function(peer) {
|
||||
var result = peer.broadcast(msg);
|
||||
if (!result) return;
|
||||
if (!result)
|
||||
return;
|
||||
|
||||
result[0].once('request', function() {
|
||||
e.emit('ack', peer);
|
||||
callback();
|
||||
});
|
||||
|
||||
result[0].once('reject', function(payload) {
|
||||
e.emit('reject', payload, peer);
|
||||
callback(new Error('TX was rejected: ' + payload.reason));
|
||||
});
|
||||
});
|
||||
|
||||
@ -1916,7 +1886,7 @@ Pool.prototype.isMisbehaving = function isMisbehaving(host) {
|
||||
|
||||
Pool.prototype.reject = function reject(peer, obj, reason, dos) {
|
||||
if (dos != null)
|
||||
this.setMisbehavior(peer, dos);
|
||||
peer.setMisbehavior(dos);
|
||||
|
||||
utils.debug('Rejecting %s %s: reason=%s',
|
||||
obj.type, obj.hash('hex'), reason);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user