fixes. refactor.

This commit is contained in:
Christopher Jeffrey 2016-03-21 18:41:06 -07:00
parent eb569ae12d
commit b792976586
6 changed files with 119 additions and 74 deletions

View File

@ -169,7 +169,7 @@ Mempool.prototype.addBlock = function addBlock(block) {
callback = utils.ensure(callback);
// Remove now-mined transactions
// XXX should batch this
utils.forEachSerial(block.txs.slice().reverse(), function(tx, next) {
utils.forEachSerial(block.txs, function(tx, next) {
self.tx.remove(tx, next);
}, callback);
};
@ -178,7 +178,7 @@ Mempool.prototype.removeBlock = function removeBlock(block, callback) {
var self = this;
callback = utils.ensure(callback);
// XXX should batch this
utils.forEachSerial(block.txs, function(tx, next) {
utils.forEachSerial(block.txs.slice().reverse(), function(tx, next) {
self.tx.add(tx, next);
}, callback);
};
@ -187,7 +187,7 @@ Mempool.prototype.get =
Mempool.prototype.getTX = function getTX(hash, callback) {
if (hash instanceof bcoin.tx)
hash = hash.hash('hex');
return this.tx.getTX(hash, index, callback);
return this.tx.getTX(hash, callback);
};
Mempool.prototype.getCoin = function getCoin(hash, index, callback) {
@ -246,15 +246,15 @@ Mempool.prototype.addTX = function addTX(tx, peer, callback, force) {
return callback(new Error('CheckTransaction failed'));
if (tx.isCoinbase()) {
this.reject(peer, tx, 'coinbase', 100);
peer.sendReject(tx, 'coinbase', 100);
return callback(new Error('coinbase as individual tx'));
}
ts = utils.now();
height = this.chain.height + 1;
if (self.requireStandard && !tx.isStandard(flags, ts, height, ret)) {
self.reject(peer, tx, ret.reason, 0);
if (this.requireStandard && !tx.isStandard(flags, ts, height, ret)) {
peer.sendReject(tx, ret.reason, 0);
return callback(new Error('TX is not standard.'));
}
@ -281,50 +281,50 @@ Mempool.prototype.addTX = function addTX(tx, peer, callback, force) {
return callback(new Error('TX inputs are not standard.'));
if (tx.getSigops(true) > constants.script.maxSigops) {
self.reject(peer, tx, 'bad-txns-too-many-sigops', 0);
peer.sendReject(tx, 'bad-txns-too-many-sigops', 0);
return callback(new Error('TX has too many sigops.'));
}
total = new bn(0);
for (i = 0; i < tx.inputs.length; i++) {
input = tx.inputs[i];
coin = input.coin;
coin = input.output;
if (coin.isCoinbase()) {
if (self.chain.height - coin.height < constants.tx.coinbaseMaturity) {
self.reject(peer, tx, 'bad-txns-premature-spend-of-coinbase', 0);
peer.sendReject(tx, 'bad-txns-premature-spend-of-coinbase', 0);
return callback(new Error('Tried to spend coinbase prematurely.'));
}
}
if (coin.value.cmpn(0) < 0 || coin.value.cmp(constants.maxMoney) > 0)
return self.reject(peer, tx, 'bad-txns-inputvalues-outofrange', 100);
return peer.sendReject(tx, 'bad-txns-inputvalues-outofrange', 100);
total.iadd(coin.value);
}
if (total.cmpn(0) < 0 || total.cmp(constants.maxMoney) > 0)
return self.reject(peer, tx, 'bad-txns-inputvalues-outofrange', 100);
return peer.sendReject(tx, 'bad-txns-inputvalues-outofrange', 100);
if (tx.getOutputValue().cmp(total) > 0) {
self.reject(peer, tx, 'bad-txns-in-belowout', 100);
peer.sendReject(tx, 'bad-txns-in-belowout', 100);
return callback(new Error('TX is spending coins it does not have.'));
}
fee = total.subn(tx.getOutputValue());
if (fee.cmpn(0) < 0) {
self.reject(peer, tx, 'bad-txns-fee-negative', 100);
peer.sendReject(tx, 'bad-txns-fee-negative', 100);
return callback(new Error('TX has a negative fee.'));
}
if (fee.cmp(constants.maxMoney) > 0) {
return self.reject(peer, tx, 'bad-txns-fee-outofrange', 100);
peer.sendReject(tx, 'bad-txns-fee-outofrange', 100);
return callback(new Error('TX has a fee higher than max money.'));
}
if (self.limitFree && fee.cmp(tx.getMinFee(true)) < 0) {
self.reject(peer, tx, 'insufficient fee', 0);
peer.sendReject(tx, 'insufficient fee', 0);
return callback(new Error('Insufficient fee.'));
}
@ -338,7 +338,7 @@ Mempool.prototype.addTX = function addTX(tx, peer, callback, force) {
self.lastTime = now;
if (self.freeCount > self.limitFreeRelay * 10 * 1000) {
self.reject(peer, tx, 'insufficient priority', 0);
peer.sendReject(tx, 'insufficient priority', 0);
return callback(new Error('Too many free txs at once!'));
}
@ -355,14 +355,14 @@ Mempool.prototype.addTX = function addTX(tx, peer, callback, force) {
if (!result) {
// Just say it's non-mandatory for now.
self.reject(peer, tx, 'non-mandatory-script-verify-flag', 0);
peer.sendReject(tx, 'non-mandatory-script-verify-flag', 0);
return callback(new Error('TX did not verify.'));
}
self.tx.add(tx, function(err) {
if (err) {
if (err.message === 'Transaction is double-spending.') {
self.reject(peer, tx, 'bad-txns-inputs-spent', 0);
peer.sendReject(tx, 'bad-txns-inputs-spent', 0);
}
return callback(err);
}
@ -389,17 +389,17 @@ Mempool.prototype.removeTX = function removeTX(hash, callback, force) {
return;
function getTX() {
if (hash.hash) {
hash = hash.hash('hex');
return self.getTX(hash, function(err, tx) {
if (err)
return callback(err);
if (!tx)
return callback();
return self.node.fillTX(hash, callback);
});
}
return callback(null, hash);
if (hash instanceof bcoin.tx)
return callback(null, hash);
hash = hash.hash('hex');
return self.getTX(hash, function(err, tx) {
if (err)
return callback(err);
if (!tx)
return callback();
return self.node.fillTX(hash, callback);
});
}
getTX(function(err, tx) {
@ -421,64 +421,47 @@ Mempool.prototype.checkTX = function checkTX(tx, peer) {
var uniq = {};
if (tx.inputs.length === 0)
return this.reject(peer, tx, 'bad-txns-vin-empty', 100);
return peer.sendReject(tx, 'bad-txns-vin-empty', 100);
if (tx.outputs.length === 0)
return this.reject(peer, tx, 'bad-txns-vout-empty', 100);
return peer.sendReject(tx, 'bad-txns-vout-empty', 100);
if (tx.getSize() > constants.block.maxSize)
return this.reject(peer, tx, 'bad-txns-oversize', 100);
return peer.sendReject(tx, 'bad-txns-oversize', 100);
for (i = 0; i < tx.outputs.length; i++) {
output = tx.outputs[i];
if (output.value.cmpn(0) < 0)
return this.reject(peer, tx, 'bad-txns-vout-negative', 100);
return peer.sendReject(tx, 'bad-txns-vout-negative', 100);
if (output.value.cmp(constants.maxMoney) > 0)
return this.reject(peer, tx, 'bad-txns-vout-toolarge', 100);
return peer.sendReject(tx, 'bad-txns-vout-toolarge', 100);
total.iadd(output.value);
if (total.cmpn(0) < 0 || total.cmp(constants.maxMoney))
return this.reject(peer, tx, 'bad-txns-txouttotal-toolarge', 100);
if (total.cmpn(0) < 0 || total.cmp(constants.maxMoney) > 0)
return peer.sendReject(tx, 'bad-txns-txouttotal-toolarge', 100);
}
for (i = 0; i < tx.inputs.length; i++) {
input = tx.inputs[i];
if (uniq[input.out.hash])
return this.reject(peer, tx, 'bad-txns-inputs-duplicate', 100);
uniq[input.out.hash] = true;
if (uniq[input.prevout.hash])
return peer.sendReject(tx, 'bad-txns-inputs-duplicate', 100);
uniq[input.prevout.hash] = true;
}
if (tx.isCoinbase()) {
size = bcoin.script.getSize(tx.inputs[0].script);
if (size < 2 || size > 100)
return this.reject(peer, tx, 'bad-cb-length', 100);
return peer.sendReject(tx, 'bad-cb-length', 100);
} else {
for (i = 0; i < tx.inputs.length; i++) {
input = tx.inputs[i];
if (+input.out.hash === 0)
return this.reject(peer, tx, 'bad-txns-prevout-null', 10);
if (+input.prevout.hash === 0)
return peer.sendReject(tx, 'bad-txns-prevout-null', 10);
}
}
return true;
};
Mempool.prototype.reject = function reject(peer, obj, reason, dos) {
utils.debug('Rejecting TX %s. Reason=%s.', obj.hash('hex'), reason);
if (dos != null)
this.node.pool.setMisbehavior(peer, dos);
if (!peer)
return false;
// peer.reject({
// reason: reason,
// data: obj.hash ? obj.hash() : []
// });
return false;
};
/**
* Expose
*/

View File

@ -585,8 +585,8 @@ MTX.prototype.isSigned = function isSigned(m) {
return false;
} else if (prev.isMultisig()) {
// Grab `m` value (number of required sigs).
m = prev[0];
if (Array.isArray(m))
m = prev.code[0];
if (Buffer.isBuffer(m))
m = m[0] || 0;
// Ensure all members are signatures.
@ -709,7 +709,7 @@ MTX.prototype.maxSize = function maxSize(maxM, maxN) {
// here since it will be ignored by
// the isMultisig clause.
// OP_PUSHDATA2 [redeem]
prev = this.inputs[i].getRedeem();
prev = this.inputs[i].script.getRedeem();
size += utils.sizeIntv(prev.getSize()) + prev.getSize();
}

View File

@ -681,6 +681,18 @@ Peer.prototype.reject = function reject(details) {
this._write(this.framer.reject(details));
};
Peer.prototype.isMisbehaving = function isMisbehaving() {
return this.pool.isMisbehaving(this.host);
};
Peer.prototype.setMisbehavior = function setMisbehavior(dos) {
return this.pool.setMisbehavior(this, dos);
};
Peer.prototype.sendReject = function sendReject(obj, reason, dos) {
return this.pool.reject(this, obj, reason, dos);
};
/**
* Expose
*/

View File

@ -172,12 +172,17 @@ Pool.prototype._init = function _init() {
}
this.chain.on('block', function(block, entry, peer) {
self.emit('block', block, peer);
// Emit merkle txs after the fact
if (block.type === 'merkleblock') {
block.txs.forEach(function(tx) {
self._handleTX(tx, peer);
utils.forEachSerial(block.txs, function(tx, next) {
self._handleTX(tx, peer, next);
}, function(err) {
if (err)
return self.emit('error', err);
self.emit('block', block, peer);
});
} else {
self.emit('block', block, peer);
}
});
@ -384,6 +389,7 @@ Pool.prototype._startInterval = function _startInterval() {
function load() {
if (!self.syncing)
return;
utils.debug('Stall recovery: loading again.');
// self._load();
}
@ -672,11 +678,11 @@ Pool.prototype._handleBlocks = function _handleBlocks(hashes, peer, callback) {
Pool.prototype._handleInv = function _handleInv(hashes, peer, callback) {
var self = this;
callback = utils.ensure(callback);
// Ignore for now if we're still syncing
if (!this.synced)
return;
callback = utils.ensure(callback);
return callback();
utils.forEachSerial(hashes, function(hash, next) {
hash = utils.toHex(hash);
@ -823,7 +829,7 @@ Pool.prototype._createPeer = function _createPeer(options) {
});
peer.on('tx', function(tx) {
self._handleTX(tx);
self._handleTX(tx, peer);
});
peer.on('addr', function(data) {
@ -1911,6 +1917,24 @@ Pool.prototype.isMisbehaving = function isMisbehaving(host) {
return false;
};
Pool.prototype.reject = function reject(peer, obj, reason, dos) {
if (dos != null)
this.setMisbehavior(peer, dos);
utils.debug('Rejecting %s %s: reason=%s',
obj.type, obj.hash('hex'), reason);
if (!peer)
return false;
// peer.reject({
// reason: reason,
// data: obj.hash()
// });
return false;
};
/**
* LoadRequest
*/

View File

@ -647,6 +647,9 @@ TX.prototype._getSigops = function _getSigops(scriptHash, accurate) {
prev = input.script.getRedeem();
if (!prev)
return;
total += prev.getSigops(true);
}
}, this);
@ -672,7 +675,7 @@ TX.prototype.getSigops = function getSigops(scriptHash, accurate) {
if (prev.isScripthash())
prev = input.script.getRedeem();
if (prev.isWitnessScripthash()) {
if (prev && prev.isWitnessScripthash()) {
prev = input.witness.getRedeem();
cost += prev.getSigops(true);
} else {
@ -694,6 +697,7 @@ TX.prototype.getSigops = function getSigops(scriptHash, accurate) {
TX.prototype.isStandard = function isStandard(flags, ts, height, ret) {
var i, input, output, type;
var nulldata = 0;
var maxVersion = constants.tx.version;
if (!ret)
ret = { reason: null };
@ -701,13 +705,16 @@ TX.prototype.isStandard = function isStandard(flags, ts, height, ret) {
if (flags == null)
flags = constants.flags.STANDARD_VERIFY_FLAGS;
if (this.version > constants.tx.version || this.version < 1) {
if (flags & constants.flags.VERIFY_CHECKSEQUENCEVERIFY)
maxVersion = Math.max(maxVersion, 2);
if (this.version < 1 || this.version > maxVersion) {
ret.reason = 'version';
return false;
}
if (ts != null) {
if (!tx.isFinal(ts, height)) {
if (!this.isFinal(ts, height)) {
ret.reason = 'non-final';
return false;
}

View File

@ -1440,7 +1440,17 @@ TXPool.prototype.getTX = function getTX(hash, callback) {
return callback();
return callback(err);
}
return callback(null, bcoin.tx.fromExtended(tx));
if (!tx)
return callback();
try {
tx = bcoin.tx.fromExtended(tx);
} catch (e) {
return callback(e);
}
return callback(null, tx);
});
};
@ -1455,7 +1465,16 @@ TXPool.prototype.getCoin = function getCoin(hash, index, callback) {
return callback(err);
}
return callback(null, bcoin.coin.fromExtended(coin));
if (!coin)
return callback();
try {
coin = bcoin.coin.fromExtended(coin);
} catch (e) {
return callback(e);
}
return callback(null, coin);
});
};