diff --git a/lib/bcoin/peer.js b/lib/bcoin/peer.js index dc8fda6b..d2140de0 100644 --- a/lib/bcoin/peer.js +++ b/lib/bcoin/peer.js @@ -45,6 +45,10 @@ function Peer(pool, createConnection, options) { this.challenge = null; this.lastPong = 0; + this.banscore = 0; + this.orphans = 0; + this.orphanTime = 0; + this.socket = createConnection.call(pool, this, options); if (!this.socket) throw new Error('No socket'); @@ -103,7 +107,7 @@ Peer.prototype._init = function init() { this.socket.once('error', function(err) { self._error(err); - self.pool.misbehaving(self, 100); + self.pool.setMisbehavior(self, 100); }); this.socket.once('close', function() { @@ -123,7 +127,7 @@ Peer.prototype._init = function init() { self._error(err); // Something is wrong here. // Ignore this peer. - self.pool.misbehaving(self, 100); + self.pool.setMisbehavior(self, 100); }); if (this.pool.options.fullNode) { diff --git a/lib/bcoin/pool.js b/lib/bcoin/pool.js index d17e0bdb..4d8af57c 100644 --- a/lib/bcoin/pool.js +++ b/lib/bcoin/pool.js @@ -194,7 +194,7 @@ Pool.prototype._init = function _init() { // If we failed a checkpoint, peer is misbehaving. if (data.checkpoint) { - self.misbehaving(peer, 100); + self.setMisbehavior(peer, 100); return; } @@ -215,7 +215,7 @@ Pool.prototype._init = function _init() { if (!peer) return; - self.misbehaving(peer, 100); + self.setMisbehavior(peer, 100); }); this.options.wallets.forEach(function(w) { @@ -292,7 +292,7 @@ Pool.prototype._stopInterval = function _stopInterval() { Pool.prototype.createConnection = function createConnection(peer, options) { var addr, net, socket; - addr = this.getSeed(options.priority, true); + addr = this.getSeed(options.priority); assert(addr); assert(addr.host); @@ -441,7 +441,7 @@ Pool.prototype._handleHeaders = function _handleHeaders(headers, peer) { peer.host); if (headers.length > 2000) { - this.misbehaving(peer, 100); + this.setMisbehavior(peer, 100); return; } @@ -479,7 +479,7 @@ Pool.prototype._handleHeaders = function _handleHeaders(headers, peer) { // simply tries to find the latest block in // the peer's chain. if (last && headers.length === 2000) - peer.loadHeaders(this.chain.locatorHashes(last), null); + peer.loadHeaders(this.chain.getLocator(last), null); // Reset interval to avoid calling getheaders unnecessarily this._startInterval(); @@ -499,7 +499,7 @@ Pool.prototype._handleBlocks = function _handleBlocks(hashes, peer) { peer.host); if (hashes.length > 500) { - this.misbehaving(peer, 100); + this.setMisbehavior(peer, 100); return; } @@ -511,15 +511,15 @@ Pool.prototype._handleBlocks = function _handleBlocks(hashes, peer) { if (this.chain.hasOrphan(hash)) { // Make sure the peer doesn't send us // more than 200 orphans every 3 minutes. - if (this.orphaning(peer)) { + if (this.isOrphaning(peer)) { utils.debug('Peer is orphaning (%s)', peer.host); - this.misbehaving(peer, 100); + this.setMisbehavior(peer, 100); return; } // Resolve orphan chain. peer.loadBlocks( - this.chain.locatorHashes(), + this.chain.getLocator(), this.chain.getOrphanRoot(hash) ); continue; @@ -553,7 +553,7 @@ Pool.prototype._handleInv = function _handleInv(hashes, peer) { hash = utils.toHex(hashes[i]); if (!this.chain.has(hash)) { if (this.options.headers) - this.peers.load.loadHeaders(this.chain.locatorHashes(), hash); + this.peers.load.loadHeaders(this.chain.getLocator(), hash); else this._request(peer, this.block.type, hash); } @@ -579,7 +579,7 @@ Pool.prototype._handleBlock = function _handleBlock(block, peer) { // Someone might be sending us bad blocks to DoS us. if (this.block.invalid[block.hash('hex')]) { utils.debug('Peer is sending an invalid chain (%s)', peer.host); - this.misbehaving(peer, 100); + this.setMisbehavior(peer, 100); return false; } @@ -589,14 +589,14 @@ Pool.prototype._handleBlock = function _handleBlock(block, peer) { utils.debug( 'Peer is sending an invalid continuation chain (%s)', peer.host); - this.misbehaving(peer, 100); + this.setMisbehavior(peer, 100); return false; } // Ignore if we already have. if (this.chain.has(block)) { utils.debug('Already have block %s (%s)', block.height, peer.host); - this.misbehaving(peer, 1); + this.setMisbehavior(peer, 1); return false; } @@ -606,7 +606,7 @@ Pool.prototype._handleBlock = function _handleBlock(block, peer) { 'Block verification failed for %s (%s)', block.rhash, peer.host); this.block.invalid[block.hash('hex')] = true; - this.misbehaving(peer, 100); + this.setMisbehavior(peer, 100); return false; } @@ -627,9 +627,9 @@ Pool.prototype._handleBlock = function _handleBlock(block, peer) { // Make sure the peer doesn't send us // more than 200 orphans every 3 minutes. - if (this.orphaning(peer)) { + if (this.isOrphaning(peer)) { utils.debug('Peer is orphaning (%s)', peer.host); - this.misbehaving(peer, 100); + this.setMisbehavior(peer, 100); return false; } @@ -642,7 +642,7 @@ Pool.prototype._handleBlock = function _handleBlock(block, peer) { // Resolve orphan chain. this.peers.load.loadBlocks( - this.chain.locatorHashes(), + this.chain.getLocator(), this.chain.getOrphanRoot(block) ); @@ -658,7 +658,7 @@ Pool.prototype._handleBlock = function _handleBlock(block, peer) { // Increase banscore by 10 if we're using getheaders. if (!this.options.multiplePeers) { - if (this.misbehaving(peer, 10)) + if (this.setMisbehavior(peer, 10)) return false; } } @@ -699,12 +699,15 @@ Pool.prototype._load = function _load() { } if (this.options.headers) - this.peers.load.loadHeaders(this.chain.locatorHashes(), null); + this.peers.load.loadHeaders(this.chain.getLocator(), null); else - this.peers.load.loadBlocks(this.chain.locatorHashes(), null); + this.peers.load.loadBlocks(this.chain.getLocator(), null); }; Pool.prototype.loadMempool = function loadMempool() { + if (this.peers.load) + this.peers.load.loadMempool(); + this.peers.block.forEach(function(peer) { peer.loadMempool(); }); @@ -719,8 +722,6 @@ Pool.prototype._createPeer = function _createPeer(priority) { priority: priority }); - peer._retry = 0; - peer.on('error', function(err) { self.emit('error', err, peer); }); @@ -747,12 +748,10 @@ Pool.prototype._createPeer = function _createPeer(priority) { }); peer.on('tx', function(tx) { - var state = self.tx.state[tx.hash('hex')]; + var requested = self._response(tx); + var added = self._addTX(tx, 1); - self._response(tx); - self._addTX(tx, 1); - - if (state !== 1 || tx.block) + if (added || tx.block) self.emit('tx', tx, peer); if (!self.options.fullNode && tx.block) @@ -760,6 +759,9 @@ Pool.prototype._createPeer = function _createPeer(priority) { }); peer.on('addr', function(data) { + if (self.options.discoverPeers === false) + return; + if (self.seeds.length > 1000) self.setSeeds(self.seeds.slice(-500)); @@ -1482,14 +1484,14 @@ Pool.prototype.getPeer = function getPeer(addr) { } }; -Pool.prototype.getSeed = function getSeed(priority, connecting) { +Pool.prototype.getSeed = function getSeed(priority) { var i, addr; var original = this.originalSeeds; var seeds = this.seeds; var all = original.concat(seeds); // Hang back if we don't have a loader peer yet. - if (!connecting && !priority && !this.peers.load) + if (!priority && !this.peers.load) return; // Randomize the non-original peers. @@ -1551,7 +1553,7 @@ Pool.prototype.getSeed = function getSeed(priority, connecting) { // If we have no block peers, always return // an address. if (!priority) { - if (all.length === 1 || connecting) + if (all.length === 1) return all[Math.random() * (all.length - 1) | 0]; } @@ -1561,22 +1563,13 @@ Pool.prototype.getSeed = function getSeed(priority, connecting) { utils.debug( 'We had to connect to a random peer. Something is not right.'); - return original[Math.random() * (original.length - 1) | 0]; + return all[Math.random() * (all.length - 1) | 0]; } }; Pool.prototype.setSeeds = function setSeeds(seeds) { this.seeds = []; this.hosts = {}; - - // Remove all seeds from misbehaving aside - // from original seeds that may be in it. - // this.peers.misbehaving = this.originalSeeds.reduce(function(out, addr) { - // if (this.peers.misbehaving[addr.host]) - // out[addr.host] = this.peers.misbehaving[addr.host]; - // return out; - // }, {}, this); - seeds.forEach(function(seed) { this.addSeed(seed); }, this); @@ -1593,7 +1586,7 @@ Pool.prototype.addSeed = function addSeed(seed) { port: seed.port }); - this.hosts[seed.host] = this.seeds.length - 1; + this.hosts[seed.host] = true; return true; }; @@ -1604,40 +1597,36 @@ Pool.prototype.removeSeed = function removeSeed(seed) { if (this.hosts[seed.host] == null) return false; - this.seeds.splice(this.hosts[seed.host], 1); + for (i = 0; i < this.seeds.length; i++) { + if (this.seeds[i].host === seed.host) { + this.seeds.splice(i, 1); + break; + } + } delete this.hosts[seed.host]; return true; }; -Pool.prototype.orphaning = function orphaning(peer) { - if (!peer._orphanTime) - peer._orphanTime = utils.now(); - - if (!peer._orphans) - peer._orphans = 0; - - if (utils.now() > peer._orphanTime + 3 * 60) { - peer._orphans = 0; - peer._orphanTime = utils.now(); +Pool.prototype.isOrphaning = function isOrphaning(peer) { + if (utils.now() > peer.orphanTime + 3 * 60) { + peer.orphans = 0; + peer.orphanTime = utils.now(); } - peer._orphans += 1; + peer.orphans += 1; - if (peer._orphans > 200) + if (peer.orphans > 200) return true; return false; }; -Pool.prototype.misbehaving = function misbehaving(peer, dos) { - if (!peer._banscore) - peer._banscore = 0; +Pool.prototype.setMisbehavior = function setMisbehavior(peer, dos) { + peer.banscore += dos; - peer._banscore += dos; - - if (peer._banscore >= constants.banScore) { + if (peer.banscore >= constants.banScore) { this.peers.misbehaving[peer.host] = utils.now(); utils.debug('Ban threshold exceeded for %s', peer.host); peer.destroy(); @@ -1660,7 +1649,7 @@ Pool.prototype.isMisbehaving = function isMisbehaving(host) { delete this.peers.misbehaving[host]; peer = this.getPeer(host); if (peer) - peer._banscore = 0; + peer.banscore = 0; return false; } return true; diff --git a/lib/bcoin/wallet.js b/lib/bcoin/wallet.js index 75da1b35..40f45577 100644 --- a/lib/bcoin/wallet.js +++ b/lib/bcoin/wallet.js @@ -594,30 +594,11 @@ Wallet.prototype.balance = function balance() { return this.tx.balance(); }; -Wallet.prototype.fill = function fill(tx, changeAddress, cb) { - var result, err; - - if (typeof changeAddress === 'function') { - cb = changeAddress; - changeAddress = null; - } - - cb = utils.asyncify(cb); - - result = this.fillUnspent(tx, changeAddress); - - if (!result.inputs) { - err = new Error('Not enough funds'); - err.minBalance = result.total; - cb(err); - return null; - } - - this.sign(tx); - - cb(null, tx); - - return tx; +Wallet.prototype.fill = function fill(tx, changeAddress, fee) { + var result = this.fillUnspent(tx, changeAddress, fee); + if (!result.inputs) + return false; + return true; }; Wallet.prototype.toAddress = function toAddress() { diff --git a/test/wallet-test.js b/test/wallet-test.js index bbfd3618..204dc73c 100644 --- a/test/wallet-test.js +++ b/test/wallet-test.js @@ -181,25 +181,22 @@ describe('Wallet', function() { // Create new transaction var t2 = bcoin.tx().out(w2, 5460); - w1.fill(t2, function(err) { - assert(!err); - assert(t2.verify()); + assert(w1.fill(t2)); + w1.sign(t2); + assert(t2.verify()); - assert.equal(t2.funds('in').toString(10), 16380); - // If change < dust and is added to outputs: - // assert.equal(t2.funds('out').toString(10), 6380); - // If change < dust and is added to fee: - assert.equal(t2.funds('out').toString(10), 5460); + assert.equal(t2.funds('in').toString(10), 16380); + // If change < dust and is added to outputs: + // assert.equal(t2.funds('out').toString(10), 6380); + // If change < dust and is added to fee: + assert.equal(t2.funds('out').toString(10), 5460); - // Create new transaction - var t3 = bcoin.tx().out(w2, 15000); - w1.fill(t3, function(err) { - assert(err); - assert.equal(err.minBalance.toString(10), 25000); + // Create new transaction + var t3 = bcoin.tx().out(w2, 15000); + assert(!w1.fill(t3)); + assert.equal(t3.total.toString(10), 25000); - cb(); - }); - }); + cb(); }); it('should sign multiple inputs using different keys', function(cb) { @@ -335,6 +332,7 @@ describe('Wallet', function() { assert(!send.verify()); var result = w1.fill(send); assert(result); + w1.sign(send); // printScript(send.inputs[0]);