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