more pool work. wallet.fill().

This commit is contained in:
Christopher Jeffrey 2016-02-01 10:54:11 -08:00
parent 1e027220c3
commit 999cfefe54
4 changed files with 75 additions and 103 deletions

View File

@ -45,6 +45,10 @@ function Peer(pool, createConnection, options) {
this.challenge = null; this.challenge = null;
this.lastPong = 0; this.lastPong = 0;
this.banscore = 0;
this.orphans = 0;
this.orphanTime = 0;
this.socket = createConnection.call(pool, this, options); this.socket = createConnection.call(pool, this, options);
if (!this.socket) if (!this.socket)
throw new Error('No socket'); throw new Error('No socket');
@ -103,7 +107,7 @@ Peer.prototype._init = function init() {
this.socket.once('error', function(err) { this.socket.once('error', function(err) {
self._error(err); self._error(err);
self.pool.misbehaving(self, 100); self.pool.setMisbehavior(self, 100);
}); });
this.socket.once('close', function() { this.socket.once('close', function() {
@ -123,7 +127,7 @@ Peer.prototype._init = function init() {
self._error(err); self._error(err);
// Something is wrong here. // Something is wrong here.
// Ignore this peer. // Ignore this peer.
self.pool.misbehaving(self, 100); self.pool.setMisbehavior(self, 100);
}); });
if (this.pool.options.fullNode) { if (this.pool.options.fullNode) {

View File

@ -194,7 +194,7 @@ Pool.prototype._init = function _init() {
// If we failed a checkpoint, peer is misbehaving. // If we failed a checkpoint, peer is misbehaving.
if (data.checkpoint) { if (data.checkpoint) {
self.misbehaving(peer, 100); self.setMisbehavior(peer, 100);
return; return;
} }
@ -215,7 +215,7 @@ Pool.prototype._init = function _init() {
if (!peer) if (!peer)
return; return;
self.misbehaving(peer, 100); self.setMisbehavior(peer, 100);
}); });
this.options.wallets.forEach(function(w) { this.options.wallets.forEach(function(w) {
@ -292,7 +292,7 @@ Pool.prototype._stopInterval = function _stopInterval() {
Pool.prototype.createConnection = function createConnection(peer, options) { Pool.prototype.createConnection = function createConnection(peer, options) {
var addr, net, socket; var addr, net, socket;
addr = this.getSeed(options.priority, true); addr = this.getSeed(options.priority);
assert(addr); assert(addr);
assert(addr.host); assert(addr.host);
@ -441,7 +441,7 @@ Pool.prototype._handleHeaders = function _handleHeaders(headers, peer) {
peer.host); peer.host);
if (headers.length > 2000) { if (headers.length > 2000) {
this.misbehaving(peer, 100); this.setMisbehavior(peer, 100);
return; return;
} }
@ -479,7 +479,7 @@ 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.locatorHashes(last), null); peer.loadHeaders(this.chain.getLocator(last), null);
// Reset interval to avoid calling getheaders unnecessarily // Reset interval to avoid calling getheaders unnecessarily
this._startInterval(); this._startInterval();
@ -499,7 +499,7 @@ Pool.prototype._handleBlocks = function _handleBlocks(hashes, peer) {
peer.host); peer.host);
if (hashes.length > 500) { if (hashes.length > 500) {
this.misbehaving(peer, 100); this.setMisbehavior(peer, 100);
return; return;
} }
@ -511,15 +511,15 @@ Pool.prototype._handleBlocks = function _handleBlocks(hashes, peer) {
if (this.chain.hasOrphan(hash)) { if (this.chain.hasOrphan(hash)) {
// 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.orphaning(peer)) { if (this.isOrphaning(peer)) {
utils.debug('Peer is orphaning (%s)', peer.host); utils.debug('Peer is orphaning (%s)', peer.host);
this.misbehaving(peer, 100); this.setMisbehavior(peer, 100);
return; return;
} }
// Resolve orphan chain. // Resolve orphan chain.
peer.loadBlocks( peer.loadBlocks(
this.chain.locatorHashes(), this.chain.getLocator(),
this.chain.getOrphanRoot(hash) this.chain.getOrphanRoot(hash)
); );
continue; continue;
@ -553,7 +553,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.locatorHashes(), hash); this.peers.load.loadHeaders(this.chain.getLocator(), hash);
else else
this._request(peer, this.block.type, hash); 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. // Someone might be sending us bad blocks to DoS us.
if (this.block.invalid[block.hash('hex')]) { if (this.block.invalid[block.hash('hex')]) {
utils.debug('Peer is sending an invalid chain (%s)', peer.host); utils.debug('Peer is sending an invalid chain (%s)', peer.host);
this.misbehaving(peer, 100); this.setMisbehavior(peer, 100);
return false; return false;
} }
@ -589,14 +589,14 @@ Pool.prototype._handleBlock = function _handleBlock(block, peer) {
utils.debug( utils.debug(
'Peer is sending an invalid continuation chain (%s)', 'Peer is sending an invalid continuation chain (%s)',
peer.host); peer.host);
this.misbehaving(peer, 100); this.setMisbehavior(peer, 100);
return false; return false;
} }
// Ignore if we already have. // Ignore if we already have.
if (this.chain.has(block)) { if (this.chain.has(block)) {
utils.debug('Already have block %s (%s)', block.height, peer.host); utils.debug('Already have block %s (%s)', block.height, peer.host);
this.misbehaving(peer, 1); this.setMisbehavior(peer, 1);
return false; return false;
} }
@ -606,7 +606,7 @@ Pool.prototype._handleBlock = function _handleBlock(block, peer) {
'Block verification failed for %s (%s)', 'Block verification failed for %s (%s)',
block.rhash, peer.host); block.rhash, peer.host);
this.block.invalid[block.hash('hex')] = true; this.block.invalid[block.hash('hex')] = true;
this.misbehaving(peer, 100); this.setMisbehavior(peer, 100);
return false; return false;
} }
@ -627,9 +627,9 @@ Pool.prototype._handleBlock = function _handleBlock(block, peer) {
// 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.orphaning(peer)) { if (this.isOrphaning(peer)) {
utils.debug('Peer is orphaning (%s)', peer.host); utils.debug('Peer is orphaning (%s)', peer.host);
this.misbehaving(peer, 100); this.setMisbehavior(peer, 100);
return false; return false;
} }
@ -642,7 +642,7 @@ Pool.prototype._handleBlock = function _handleBlock(block, peer) {
// Resolve orphan chain. // Resolve orphan chain.
this.peers.load.loadBlocks( this.peers.load.loadBlocks(
this.chain.locatorHashes(), this.chain.getLocator(),
this.chain.getOrphanRoot(block) this.chain.getOrphanRoot(block)
); );
@ -658,7 +658,7 @@ Pool.prototype._handleBlock = function _handleBlock(block, peer) {
// Increase banscore by 10 if we're using getheaders. // Increase banscore by 10 if we're using getheaders.
if (!this.options.multiplePeers) { if (!this.options.multiplePeers) {
if (this.misbehaving(peer, 10)) if (this.setMisbehavior(peer, 10))
return false; return false;
} }
} }
@ -699,12 +699,15 @@ Pool.prototype._load = function _load() {
} }
if (this.options.headers) if (this.options.headers)
this.peers.load.loadHeaders(this.chain.locatorHashes(), null); this.peers.load.loadHeaders(this.chain.getLocator(), null);
else else
this.peers.load.loadBlocks(this.chain.locatorHashes(), null); this.peers.load.loadBlocks(this.chain.getLocator(), null);
}; };
Pool.prototype.loadMempool = function loadMempool() { Pool.prototype.loadMempool = function loadMempool() {
if (this.peers.load)
this.peers.load.loadMempool();
this.peers.block.forEach(function(peer) { this.peers.block.forEach(function(peer) {
peer.loadMempool(); peer.loadMempool();
}); });
@ -719,8 +722,6 @@ Pool.prototype._createPeer = function _createPeer(priority) {
priority: priority priority: priority
}); });
peer._retry = 0;
peer.on('error', function(err) { peer.on('error', function(err) {
self.emit('error', err, peer); self.emit('error', err, peer);
}); });
@ -747,12 +748,10 @@ Pool.prototype._createPeer = function _createPeer(priority) {
}); });
peer.on('tx', function(tx) { 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); if (added || tx.block)
self._addTX(tx, 1);
if (state !== 1 || tx.block)
self.emit('tx', tx, peer); self.emit('tx', tx, peer);
if (!self.options.fullNode && tx.block) if (!self.options.fullNode && tx.block)
@ -760,6 +759,9 @@ Pool.prototype._createPeer = function _createPeer(priority) {
}); });
peer.on('addr', function(data) { peer.on('addr', function(data) {
if (self.options.discoverPeers === false)
return;
if (self.seeds.length > 1000) if (self.seeds.length > 1000)
self.setSeeds(self.seeds.slice(-500)); 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 i, addr;
var original = this.originalSeeds; var original = this.originalSeeds;
var seeds = this.seeds; var seeds = this.seeds;
var all = original.concat(seeds); var all = original.concat(seeds);
// Hang back if we don't have a loader peer yet. // Hang back if we don't have a loader peer yet.
if (!connecting && !priority && !this.peers.load) if (!priority && !this.peers.load)
return; return;
// Randomize the non-original peers. // Randomize the non-original peers.
@ -1551,7 +1553,7 @@ Pool.prototype.getSeed = function getSeed(priority, connecting) {
// If we have no block peers, always return // If we have no block peers, always return
// an address. // an address.
if (!priority) { if (!priority) {
if (all.length === 1 || connecting) if (all.length === 1)
return all[Math.random() * (all.length - 1) | 0]; return all[Math.random() * (all.length - 1) | 0];
} }
@ -1561,22 +1563,13 @@ Pool.prototype.getSeed = function getSeed(priority, connecting) {
utils.debug( utils.debug(
'We had to connect to a random peer. Something is not right.'); '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) { Pool.prototype.setSeeds = function setSeeds(seeds) {
this.seeds = []; this.seeds = [];
this.hosts = {}; 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) { seeds.forEach(function(seed) {
this.addSeed(seed); this.addSeed(seed);
}, this); }, this);
@ -1593,7 +1586,7 @@ Pool.prototype.addSeed = function addSeed(seed) {
port: seed.port port: seed.port
}); });
this.hosts[seed.host] = this.seeds.length - 1; this.hosts[seed.host] = true;
return true; return true;
}; };
@ -1604,40 +1597,36 @@ Pool.prototype.removeSeed = function removeSeed(seed) {
if (this.hosts[seed.host] == null) if (this.hosts[seed.host] == null)
return false; 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]; delete this.hosts[seed.host];
return true; return true;
}; };
Pool.prototype.orphaning = function orphaning(peer) { Pool.prototype.isOrphaning = function isOrphaning(peer) {
if (!peer._orphanTime) if (utils.now() > peer.orphanTime + 3 * 60) {
peer._orphanTime = utils.now(); peer.orphans = 0;
peer.orphanTime = utils.now();
if (!peer._orphans)
peer._orphans = 0;
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 true;
return false; return false;
}; };
Pool.prototype.misbehaving = function misbehaving(peer, dos) { Pool.prototype.setMisbehavior = function setMisbehavior(peer, dos) {
if (!peer._banscore) peer.banscore += dos;
peer._banscore = 0;
peer._banscore += dos; if (peer.banscore >= constants.banScore) {
if (peer._banscore >= constants.banScore) {
this.peers.misbehaving[peer.host] = utils.now(); this.peers.misbehaving[peer.host] = utils.now();
utils.debug('Ban threshold exceeded for %s', peer.host); utils.debug('Ban threshold exceeded for %s', peer.host);
peer.destroy(); peer.destroy();
@ -1660,7 +1649,7 @@ Pool.prototype.isMisbehaving = function isMisbehaving(host) {
delete this.peers.misbehaving[host]; delete this.peers.misbehaving[host];
peer = this.getPeer(host); peer = this.getPeer(host);
if (peer) if (peer)
peer._banscore = 0; peer.banscore = 0;
return false; return false;
} }
return true; return true;

View File

@ -594,30 +594,11 @@ Wallet.prototype.balance = function balance() {
return this.tx.balance(); return this.tx.balance();
}; };
Wallet.prototype.fill = function fill(tx, changeAddress, cb) { Wallet.prototype.fill = function fill(tx, changeAddress, fee) {
var result, err; var result = this.fillUnspent(tx, changeAddress, fee);
if (!result.inputs)
if (typeof changeAddress === 'function') { return false;
cb = changeAddress; return true;
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.toAddress = function toAddress() { Wallet.prototype.toAddress = function toAddress() {

View File

@ -181,25 +181,22 @@ describe('Wallet', function() {
// Create new transaction // Create new transaction
var t2 = bcoin.tx().out(w2, 5460); var t2 = bcoin.tx().out(w2, 5460);
w1.fill(t2, function(err) { assert(w1.fill(t2));
assert(!err); w1.sign(t2);
assert(t2.verify()); assert(t2.verify());
assert.equal(t2.funds('in').toString(10), 16380); assert.equal(t2.funds('in').toString(10), 16380);
// If change < dust and is added to outputs: // If change < dust and is added to outputs:
// assert.equal(t2.funds('out').toString(10), 6380); // assert.equal(t2.funds('out').toString(10), 6380);
// If change < dust and is added to fee: // If change < dust and is added to fee:
assert.equal(t2.funds('out').toString(10), 5460); assert.equal(t2.funds('out').toString(10), 5460);
// Create new transaction // Create new transaction
var t3 = bcoin.tx().out(w2, 15000); var t3 = bcoin.tx().out(w2, 15000);
w1.fill(t3, function(err) { assert(!w1.fill(t3));
assert(err); assert.equal(t3.total.toString(10), 25000);
assert.equal(err.minBalance.toString(10), 25000);
cb(); cb();
});
});
}); });
it('should sign multiple inputs using different keys', function(cb) { it('should sign multiple inputs using different keys', function(cb) {
@ -335,6 +332,7 @@ describe('Wallet', function() {
assert(!send.verify()); assert(!send.verify());
var result = w1.fill(send); var result = w1.fill(send);
assert(result); assert(result);
w1.sign(send);
// printScript(send.inputs[0]); // printScript(send.inputs[0]);