diff --git a/lib/bcoin/pool.js b/lib/bcoin/pool.js index 25ce4ca4..54442178 100644 --- a/lib/bcoin/pool.js +++ b/lib/bcoin/pool.js @@ -282,11 +282,7 @@ Pool.prototype._addLoader = function _addLoader() { if (this.peers.load != null) return; - peer = new bcoin.peer(this, this.createConnection, { - backoff: 750 * Math.random(), - startHeight: this.options.startHeight, - relay: this.options.relay - }); + peer = this._createPeer(750 * Math.random()); peer.once('socket', function() { self.emit('debug', 'Added loader peer: %s', peer.host); @@ -619,6 +615,16 @@ Pool.prototype.loadMempool = function loadMempool() { }); }; +Pool.prototype._createPeer = function _createPeer(backoff) { + var peer = new bcoin.peer(this, this.createConnection, { + backoff: backoff, + startHeight: this.options.startHeight, + relay: this.options.relay + }); + peer._retry = 0; + return peer; +}; + Pool.prototype._addPeer = function _addPeer(backoff) { var self = this; var peer; @@ -634,17 +640,11 @@ Pool.prototype._addPeer = function _addPeer(backoff) { return; } - peer = new bcoin.peer(this, this.createConnection, { - backoff: backoff, - startHeight: this.options.startHeight, - relay: this.options.relay - }); + peer = this._createPeer(backoff); this.peers.pending.push(peer); this.peers.all.push(peer); - peer._retry = 0; - // Create new peer on failure peer.on('error', function(err) { self.emit('error', err, peer); @@ -690,26 +690,27 @@ Pool.prototype._addPeer = function _addPeer(backoff) { }); peer.on('merkleblock', function(block) { - // Reset backoff, peer seems to be responsive backoff = 0; self._handleBlock(block, peer); }); peer.on('block', function(block) { - // Reset backoff, peer seems to be responsive backoff = 0; self._handleBlock(block, peer); }); - // Just FYI peer.on('reject', function(payload) { + self.emit('debug', + 'Reject: msg=%s ccode=%s reason=%s data=%s', + payload.message, payload.ccode, payload.reason, + utils.toHex(payload.data)); self.emit('reject', payload, peer); }); peer.on('notfound', function(items) { items.forEach(function(item) { var req = self.request.map[utils.toHex(item.hash)]; - if (req) + if (req && req.peer === peer) req.finish(null); }); }); @@ -718,7 +719,7 @@ Pool.prototype._addPeer = function _addPeer(backoff) { self._response(tx); self.emit('tx', tx, peer); - if (!self.options.fullNode) + if (!self.options.fullNode && tx.block) self.emit('watched', tx, peer); }); diff --git a/lib/bcoin/tx.js b/lib/bcoin/tx.js index e813bfff..d9daab58 100644 --- a/lib/bcoin/tx.js +++ b/lib/bcoin/tx.js @@ -67,9 +67,6 @@ function TX(data, block) { this.ps = this.ts === 0 ? utils.now() : 0; } -TX.fee = constants.tx.fee; -TX.dust = constants.tx.dust; - TX.prototype.clone = function clone() { return new TX(this); }; @@ -170,13 +167,13 @@ TX.prototype.scriptInput = function scriptInput(index, pub, redeem) { // We should have previous outputs by now. assert(input.out.tx); - // Get the previous output's subscript - s = input.out.tx.getSubscript(input.out.index); - // Already has a script template (at least) if (input.script.length) return; + // Get the previous output's subscript + s = input.out.tx.getSubscript(input.out.index); + // P2SH if (bcoin.script.isScripthash(s)) { assert(redeem); @@ -240,13 +237,15 @@ TX.prototype.signInput = function signInput(index, key, type) { s = input.out.tx.getSubscript(input.out.index); if (bcoin.script.isScripthash(s)) { - // We need to grab the redeem script when signing p2sh transactions. + // We need to grab the redeem script when + // signing p2sh transactions. redeem = bcoin.script.decode(input.script[input.script.length - 1]); } else { redeem = s; } - // Get the hash of the current tx, minus the other inputs, plus the sighash. + // Get the hash of the current tx, minus the other + // inputs, plus the sighash type. hash = this.signatureHash(index, redeem, type); // Sign the transaction with our one input @@ -358,10 +357,9 @@ TX.prototype.signInput = function signInput(index, key, type) { } // Remove signatures which are not required. - // If just using bcoin for signing, this - // should never happen except with dealing - // with implementations that potentially - // handle signature slots differently. + // This should never happen except when dealing + // with implementations that potentially handle + // signature slots differently. while (signatures > m) { input.script.splice(len - 1, 1); signatures--; @@ -588,16 +586,20 @@ TX.prototype.signatureHash = function signatureHash(index, s, type) { // bitcoind sighash_single bug: if (index >= copy.outputs.length) return constants.oneHash.slice(); + while (copy.outputs.length < index + 1) copy.outputs.push({}); + while (copy.outputs.length > index + 1) copy.outputs.pop(); + copy.outputs.forEach(function(output, i) { if (i !== index) { output.script = []; output.value = new bn('ffffffffffffffff', 'hex'); } }); + copy.inputs.forEach(function(input, i) { if (i !== index) input.seq = 0; @@ -741,21 +743,12 @@ TX.prototype.maxSize = function maxSize() { TX.prototype.getUnspent = function getUnspent(unspent) { var tx = this.clone(); - - // NOTE: tx should be prefilled with all outputs var cost = tx.funds('out'); - - // Use initial fee for starters var fee = 1; - - // total = cost + fee - var total = cost.addn(TX.fee); - + var total = cost.addn(constants.tx.fee); var inputs = []; - var lastAdded = 0; - - var byteSize, addFee, change; + var size, addFee, change; function addInput(unspent) { // Add new inputs until TX will have enough funds to cover both @@ -771,16 +764,19 @@ TX.prototype.getUnspent = function getUnspent(unspent) { unspent.every(addInput); // Add dummy output (for `change`) to calculate maximum TX size - tx.output({ address: null, value: new bn(0) }); + tx.output({ + script: [], + value: new bn(0) + }); // Change fee value if it is more than 1024 bytes // (10000 satoshi for every 1024 bytes) do { // Calculate maximum possible size after signing - byteSize = tx.maxSize(); + size = tx.maxSize(); - addFee = Math.ceil(byteSize / 1024) - fee; - total.iaddn(addFee * TX.fee); + addFee = Math.ceil(size / 1024) - fee; + total.iaddn(addFee * constants.tx.fee); fee += addFee; // Failed to get enough funds, add more inputs @@ -788,17 +784,16 @@ TX.prototype.getUnspent = function getUnspent(unspent) { unspent.slice(lastAdded).every(addInput); } while (tx.funds('in').cmp(total) < 0 && lastAdded < unspent.length); + // Expose `total` + this.total = total; + // Still failing to get enough funds - if (tx.funds('in').cmp(total) < 0) { - this.total = total; + if (tx.funds('in').cmp(total) < 0) return null; - } // How much money is left after sending outputs change = tx.funds('in').sub(total); - this.total = total; - // Return necessary inputs and change. return { inputs: inputs, @@ -823,7 +818,7 @@ TX.prototype.fillUnspent = function fillUnspent(unspent, changeAddress) { this.input(input); }, this); - if (result.change.cmpn(TX.dust) < 0) { + if (result.change.cmpn(constants.tx.dust) < 0) { // Do nothing. Change is added to fee. assert.equal( this.getFee().toNumber(), @@ -833,10 +828,12 @@ TX.prototype.fillUnspent = function fillUnspent(unspent, changeAddress) { } else { if (!this.changeAddress) throw new Error('No change address'); + this.output({ address: this.changeAddress, value: result.change }); + this.changeOutput = this.outputs[this.outputs.length - 1]; } @@ -845,6 +842,7 @@ TX.prototype.fillUnspent = function fillUnspent(unspent, changeAddress) { TX.prototype._recalculateFee = function recalculateFee() { var output = this.changeOutput; + var size, real, fee; if (!output) { this.output({ @@ -854,28 +852,28 @@ TX.prototype._recalculateFee = function recalculateFee() { output = this.outputs[this.outputs.length - 1]; } - var byteSize = this.render().length; - var newFee = Math.ceil(byteSize / 1024) * TX.fee; - var currentFee = this.getFee().toNumber(); + size = this.render().length; + real = Math.ceil(size / 1024) * constants.tx.fee; + fee = this.getFee().toNumber(); - if (newFee === currentFee) { + if (real === fee) { if (!this.changeOutput) this.outputs.pop(); return; } - if (newFee > currentFee) { - if (output.value.cmpn(newFee - currentFee) < 0) { + if (real > fee) { + if (output.value.cmpn(real - fee) < 0) { this.outputs.pop(); this.changeOutput = null; return; } - output.value.isubn(newFee - currentFee); + output.value.isubn(real - fee); } else { - output.value.iaddn(currentFee - newFee); + output.value.iaddn(fee - real); } - if (output.value.cmpn(TX.dust) < 0) { + if (output.value.cmpn(constants.tx.dust) < 0) { this.outputs.pop(); this.changeOutput = null; return;