contextual verification. misc.
This commit is contained in:
parent
e912f78814
commit
fe46d1ada5
@ -581,8 +581,6 @@ BlockDB.prototype.getCoin = function getCoin(hash, index, callback) {
|
|||||||
var id = 'u/t/' + hash + '/' + index;
|
var id = 'u/t/' + hash + '/' + index;
|
||||||
|
|
||||||
this.index.get(id, function(err, record) {
|
this.index.get(id, function(err, record) {
|
||||||
var record;
|
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
if (err.type === 'NotFoundError')
|
if (err.type === 'NotFoundError')
|
||||||
return callback();
|
return callback();
|
||||||
@ -731,8 +729,6 @@ BlockDB.prototype.getTX = function getTX(hash, callback) {
|
|||||||
var id = 't/t/' + hash;
|
var id = 't/t/' + hash;
|
||||||
|
|
||||||
this.index.get(id, function(err, record) {
|
this.index.get(id, function(err, record) {
|
||||||
var record;
|
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
if (err.type === 'NotFoundError')
|
if (err.type === 'NotFoundError')
|
||||||
return callback();
|
return callback();
|
||||||
@ -775,8 +771,6 @@ BlockDB.prototype.getBlock = function getBlock(hash, callback) {
|
|||||||
id = 'b/h/' + value;
|
id = 'b/h/' + value;
|
||||||
|
|
||||||
this.index.get(id, function(err, record) {
|
this.index.get(id, function(err, record) {
|
||||||
var record;
|
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
if (err.type === 'NotFoundError')
|
if (err.type === 'NotFoundError')
|
||||||
return callback();
|
return callback();
|
||||||
@ -816,12 +810,34 @@ BlockDB.prototype.getBlock = function getBlock(hash, callback) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
BlockDB.prototype.hasCoin = function hasCoin(hash, index, callback) {
|
||||||
|
var id = 'u/t/' + hash + '/' + index;
|
||||||
|
|
||||||
|
this.index.get(id, function(err, record) {
|
||||||
|
if (err && err.type !== 'NotFoundError')
|
||||||
|
return callback(err);
|
||||||
|
|
||||||
|
return callback(null, record ? true : false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
BlockDB.prototype.hasTX = function hasTX(hash, callback) {
|
||||||
|
var id = 't/t/' + hash;
|
||||||
|
|
||||||
|
this.index.get(id, function(err, record) {
|
||||||
|
if (err && err.type !== 'NotFoundError')
|
||||||
|
return callback(err);
|
||||||
|
|
||||||
|
return callback(null, record ? true : false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
BlockDB.prototype.isSpent = function isSpent(hash, index, callback) {
|
BlockDB.prototype.isSpent = function isSpent(hash, index, callback) {
|
||||||
this.getCoin(hash, index, function(err, coin) {
|
return this.hasCoin(hash, index, function(err, result) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
return callback(null, coin ? false : true);
|
return callback(null, !result);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -260,6 +260,7 @@ Chain.prototype._preload = function _preload(callback) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Chain.prototype._saveBlock = function _saveBlock(block, callback) {
|
Chain.prototype._saveBlock = function _saveBlock(block, callback) {
|
||||||
|
var self = this;
|
||||||
var node = bcoin.node.global;
|
var node = bcoin.node.global;
|
||||||
|
|
||||||
if (!node)
|
if (!node)
|
||||||
@ -275,74 +276,8 @@ Chain.prototype._saveBlock = function _saveBlock(block, callback) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Chain.prototype._fillCoins = function _fillCoin(block, callback) {
|
|
||||||
var node = bcoin.node.global;
|
|
||||||
|
|
||||||
if (!node)
|
|
||||||
return callback();
|
|
||||||
|
|
||||||
node.block.fillCoins(block, callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
Chain.prototype._verifyContext = function _verifyContext(block, prev, callback) {
|
|
||||||
var node = bcoin.node.global;
|
|
||||||
|
|
||||||
if (!node)
|
|
||||||
return callback(null, block.verifyContext());
|
|
||||||
|
|
||||||
var height = prev.height + 1;
|
|
||||||
var scriptChecks = true;
|
|
||||||
|
|
||||||
node.block.fillCoins(block, function(err) {
|
|
||||||
var pending;
|
|
||||||
|
|
||||||
if (err)
|
|
||||||
return callback(err);
|
|
||||||
|
|
||||||
pending = block.txs.length;
|
|
||||||
|
|
||||||
// If we are an ancestor of a checkpoint, we can
|
|
||||||
// skip the input verification.
|
|
||||||
if (height < network.checkpoints.lastHeight && !network.checkpoints[height])
|
|
||||||
scriptChecks = false;
|
|
||||||
|
|
||||||
if (!block.verifyContext())
|
|
||||||
return callback(null, false);
|
|
||||||
|
|
||||||
if (!pending)
|
|
||||||
return callback(null, true);
|
|
||||||
|
|
||||||
// Check all transactions
|
|
||||||
block.txs.forEach(function(tx) {
|
|
||||||
var i;
|
|
||||||
for (i = 0; j < tx.inputs.length; i++) {
|
|
||||||
input = tx.inputs[i];
|
|
||||||
// Ensure tx is not double spending an output
|
|
||||||
if (!input.output) {
|
|
||||||
utils.debug('Block is using spent inputs: %s (tx: %s, output: %s)',
|
|
||||||
this.rhash, tx.hash('hex'),
|
|
||||||
input.prevout.hash + '/' + input.prevout.index);
|
|
||||||
return callback(null, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// BIP30 - Ensure there are no duplicate txids
|
|
||||||
node.block.hasTX(tx.hash('hex'), function(err, has) {
|
|
||||||
// Blocks 91842 and 91880 created duplicate
|
|
||||||
// txids by using the same exact output script
|
|
||||||
// and extraNonce.
|
|
||||||
if (has) {
|
|
||||||
utils.debug('Block is overwriting txids: %s', this.rhash);
|
|
||||||
if (!(network.type === 'main' && (height === 91842 || height === 91880)))
|
|
||||||
return callback(null, false);
|
|
||||||
}
|
|
||||||
return callback(null, true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
Chain.prototype._removeBlock = function _removeBlock(tip, callback) {
|
Chain.prototype._removeBlock = function _removeBlock(tip, callback) {
|
||||||
|
var self = this;
|
||||||
var node = bcoin.node.global;
|
var node = bcoin.node.global;
|
||||||
|
|
||||||
if (!node)
|
if (!node)
|
||||||
@ -353,9 +288,289 @@ Chain.prototype._removeBlock = function _removeBlock(tip, callback) {
|
|||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
if (!block)
|
if (!block)
|
||||||
return;
|
return callback();
|
||||||
|
|
||||||
node.mempool.removeBlock(block);
|
node.mempool.removeBlock(block);
|
||||||
|
|
||||||
|
self.emit('reorg block', block.hash('hex'));
|
||||||
|
|
||||||
|
block.txs.forEach(function(tx) {
|
||||||
|
self.emit('reorg tx', tx.hash('hex'));
|
||||||
|
});
|
||||||
|
|
||||||
|
return callback();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Chain.prototype._verifyContext = function _verifyContext(block, prev, callback) {
|
||||||
|
var self = this;
|
||||||
|
var flags;
|
||||||
|
|
||||||
|
flags = this._verify(block, prev);
|
||||||
|
|
||||||
|
if (flags === false)
|
||||||
|
return callback(null, false);
|
||||||
|
|
||||||
|
this._checkDuplicates(block, prev, function(err, result) {
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
return callback(null, false);
|
||||||
|
|
||||||
|
self._checkInputs(block, prev, flags, function(err, result) {
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
return callback(null, false);
|
||||||
|
|
||||||
|
return callback(null, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Chain.prototype._verify = function _verify(block, prev) {
|
||||||
|
// var flags = constants.flags.MANDATORY_VERIFY_FLAGS;
|
||||||
|
var flags = {};
|
||||||
|
var sigops = 0;
|
||||||
|
var height, ts, i, tx, cb, coinbaseHeight;
|
||||||
|
|
||||||
|
// Skip the genesis block
|
||||||
|
if (block.isGenesis())
|
||||||
|
return flags;
|
||||||
|
|
||||||
|
// Ensure it's not an orphan
|
||||||
|
if (!prev) {
|
||||||
|
utils.debug('Block has no previous entry: %s', block.rhash);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
height = prev.height + 1;
|
||||||
|
|
||||||
|
// Ensure the timestamp is correct
|
||||||
|
if (block.ts <= prev.getMedianTime()) {
|
||||||
|
utils.debug('Block time is lower than median: %s', block.rhash);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the miner's target is equal to what we expect
|
||||||
|
if (block.bits !== block.chain.getTarget(prev, block)) {
|
||||||
|
utils.debug('Block is using wrong target: %s', block.rhash);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only allow version 2 blocks (coinbase height)
|
||||||
|
// once the majority of blocks are using it.
|
||||||
|
if (block.version < 2 && prev.isOutdated(2)) {
|
||||||
|
utils.debug('Block is outdated (v2): %s', block.rhash);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only allow version 3 blocks (sig validation)
|
||||||
|
// once the majority of blocks are using it.
|
||||||
|
if (block.version < 3 && prev.isOutdated(3)) {
|
||||||
|
utils.debug('Block is outdated (v3): %s', block.rhash);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only allow version 4 blocks (checklocktimeverify)
|
||||||
|
// once the majority of blocks are using it.
|
||||||
|
if (block.version < 4 && prev.isOutdated(4)) {
|
||||||
|
utils.debug('Block is outdated (v4): %s', block.rhash);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only allow version 8 blocks (locktime median past)
|
||||||
|
// once the majority of blocks are using it.
|
||||||
|
// if (block.version < 8 && prev.isOutdated(8)) {
|
||||||
|
// utils.debug('Block is outdated (v8): %s', block.rhash);
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Make sure the height contained in the coinbase is correct.
|
||||||
|
if (block.version >= 2 && prev.isUpgraded(2))
|
||||||
|
coinbaseHeight = true;
|
||||||
|
|
||||||
|
// Signature validation is now enforced (bip66)
|
||||||
|
if (!(block.version >= 3 && prev.isUpgraded(3)))
|
||||||
|
flags.dersig = false;
|
||||||
|
|
||||||
|
// CHECKLOCKTIMEVERIFY is now usable (bip65)
|
||||||
|
if (!(block.version >= 4 && prev.isUpgraded(4)))
|
||||||
|
flags.checklocktimeverify = false;
|
||||||
|
|
||||||
|
// Use nLockTime median past (bip113)
|
||||||
|
// https://github.com/btcdrak/bips/blob/d4c9a236ecb947866c61aefb868b284498489c2b/bip-0113.mediawiki
|
||||||
|
// Support version bits:
|
||||||
|
// https://gist.github.com/sipa/bf69659f43e763540550
|
||||||
|
// http://lists.linuxfoundation.org/pipermail/bitcoin-dev/2015-August/010396.html
|
||||||
|
// if (block.version >= 8 && prev.isUpgraded(8))
|
||||||
|
// flags.locktimeMedian = true;
|
||||||
|
|
||||||
|
// Can't verify any further when merkleblock or headers.
|
||||||
|
if (block.subtype !== 'block')
|
||||||
|
return flags;
|
||||||
|
|
||||||
|
// Make sure the height contained in the coinbase is correct.
|
||||||
|
if (coinbaseHeight) {
|
||||||
|
cb = bcoin.script.getCoinbaseData(block.txs[0].inputs[0].script);
|
||||||
|
|
||||||
|
// Make sure the coinbase is parseable.
|
||||||
|
if (!cb) {
|
||||||
|
utils.debug('Block has malformed coinbase: %s', block.rhash);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure coinbase height is equal to the actual height.
|
||||||
|
if (cb.height !== height) {
|
||||||
|
utils.debug('Block has bad coinbase height: %s', block.rhash);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get timestamp for tx.isFinal().
|
||||||
|
ts = flags.locktimeMedian
|
||||||
|
? prev.getMedianTime()
|
||||||
|
: block.ts;
|
||||||
|
|
||||||
|
// Check all transactions
|
||||||
|
for (i = 0; i < block.txs.length; i++) {
|
||||||
|
tx = block.txs[i];
|
||||||
|
|
||||||
|
// Transactions must be finalized with
|
||||||
|
// regards to nSequence and nLockTime.
|
||||||
|
if (!tx.isFinal(height, ts)) {
|
||||||
|
utils.debug('TX is not final: %s (%s)', block.rhash, i);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for tx sigops limits
|
||||||
|
// Bitcoind does not check for this when accepting
|
||||||
|
// a block even though it probably should.
|
||||||
|
// if (tx.getSigops(true) > constants.script.maxTxSigops) {
|
||||||
|
// // Block 71036 abused checksig to
|
||||||
|
// // include a huge number of sigops.
|
||||||
|
// utils.debug('Block TX has too many sigops: %s', block.rhash);
|
||||||
|
// if (!(network.type === 'main' && height === 71036))
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Check for block sigops limits
|
||||||
|
// Start counting P2SH sigops once block
|
||||||
|
// timestamps reach March 31st, 2012.
|
||||||
|
if (block.ts >= constants.block.bip16time)
|
||||||
|
sigops += tx.getSigops(true);
|
||||||
|
else
|
||||||
|
sigops += tx.getSigops();
|
||||||
|
|
||||||
|
if (sigops > constants.script.maxBlockSigops) {
|
||||||
|
utils.debug('Block has too many sigops: %s', block.rhash);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
Chain.prototype._checkDuplicates = function _checkDuplicates(block, prev, callback) {
|
||||||
|
var node = bcoin.node.global;
|
||||||
|
var height = prev.height + 1;
|
||||||
|
var pending = block.txs.length;
|
||||||
|
var called;
|
||||||
|
|
||||||
|
if (!node || block.subtype !== 'block')
|
||||||
|
return callback(null, true);
|
||||||
|
|
||||||
|
if (block.isGenesis())
|
||||||
|
return callback(null, true);
|
||||||
|
|
||||||
|
assert(pending);
|
||||||
|
|
||||||
|
function done(err, result) {
|
||||||
|
if (called)
|
||||||
|
return;
|
||||||
|
called = true;
|
||||||
|
callback(err, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check all transactions
|
||||||
|
block.txs.forEach(function(tx) {
|
||||||
|
var hash = tx.hash('hex');
|
||||||
|
|
||||||
|
// BIP30 - Ensure there are no duplicate txids
|
||||||
|
node.block.hasTX(hash, function(err, result) {
|
||||||
|
if (called)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
return done(err);
|
||||||
|
|
||||||
|
// Blocks 91842 and 91880 created duplicate
|
||||||
|
// txids by using the same exact output script
|
||||||
|
// and extraNonce.
|
||||||
|
if (result) {
|
||||||
|
utils.debug('Block is overwriting txids: %s', block.rhash);
|
||||||
|
if (!(network.type === 'main' && (height === 91842 || height === 91880)))
|
||||||
|
return done(null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!--pending)
|
||||||
|
return done(null, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Chain.prototype._checkInputs = function _checkInputs(block, prev, flags, callback) {
|
||||||
|
var node = bcoin.node.global;
|
||||||
|
var height = prev.height + 1;
|
||||||
|
|
||||||
|
if (!node || block.subtype !== 'block')
|
||||||
|
return callback(null, true);
|
||||||
|
|
||||||
|
if (block.isGenesis())
|
||||||
|
return callback(null, true);
|
||||||
|
|
||||||
|
// If we are an ancestor of a checkpoint, we can
|
||||||
|
// skip the input verification.
|
||||||
|
if (height < network.checkpoints.lastHeight && !network.checkpoints[height])
|
||||||
|
return callback(null, true);
|
||||||
|
|
||||||
|
node.block.fillCoins(block.txs, function(err) {
|
||||||
|
var i, j, input, hash;
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
|
||||||
|
// Check all transactions
|
||||||
|
for (i = 0; i < block.txs.length; i++) {
|
||||||
|
tx = blocks.txs[i];
|
||||||
|
hash = tx.hash('hex');
|
||||||
|
|
||||||
|
for (j = 0; j < tx.inputs.length; j++) {
|
||||||
|
input = tx.inputs[j];
|
||||||
|
|
||||||
|
// Coinbases do not have prevouts
|
||||||
|
if (tx.isCoinbase())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Ensure tx is not double spending an output
|
||||||
|
if (!input.output) {
|
||||||
|
utils.debug('Block is using spent inputs: %s (tx: %s, output: %s)',
|
||||||
|
block.rhash, tx.rhash,
|
||||||
|
input.prevout.rhash + '/' + input.prevout.index);
|
||||||
|
return callback(null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the scripts
|
||||||
|
if (!tx.verify(j, true, flags)) {
|
||||||
|
utils.debug('Block has invalid inputs: %s', block.rhash);
|
||||||
|
return callback(null, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return callback(null, true);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -371,7 +586,7 @@ Chain.prototype._addEntry = function _addEntry(entry, block, callback) {
|
|||||||
return callback(null, Chain.codes.unchanged);
|
return callback(null, Chain.codes.unchanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Duplicate height
|
// Duplicate height (do a sync call here since this is cached)
|
||||||
existing = this.db.get(entry.height);
|
existing = this.db.get(entry.height);
|
||||||
if (existing && existing.hash === entry.hash)
|
if (existing && existing.hash === entry.hash)
|
||||||
return callback(null, Chain.codes.unchanged);
|
return callback(null, Chain.codes.unchanged);
|
||||||
@ -380,7 +595,7 @@ Chain.prototype._addEntry = function _addEntry(entry, block, callback) {
|
|||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
self._saveEntry(entry, function(err) {
|
self._saveEntry(entry, true, function(err) {
|
||||||
if (err)
|
if (err)
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
|
||||||
@ -389,7 +604,7 @@ Chain.prototype._addEntry = function _addEntry(entry, block, callback) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Chain.prototype._saveEntry = function _saveEntry(entry, callback) {
|
Chain.prototype._saveEntry = function _saveEntry(entry, save, callback) {
|
||||||
this.heightLookup[entry.hash] = entry.height;
|
this.heightLookup[entry.hash] = entry.height;
|
||||||
|
|
||||||
if (!this.tip || entry.height > this.tip.height) {
|
if (!this.tip || entry.height > this.tip.height) {
|
||||||
@ -397,11 +612,8 @@ Chain.prototype._saveEntry = function _saveEntry(entry, callback) {
|
|||||||
this.emit('tip', this.tip);
|
this.emit('tip', this.tip);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (callback) {
|
if (save)
|
||||||
if (typeof callback !== 'function')
|
|
||||||
callback = null;
|
|
||||||
this.db.save(entry, callback);
|
this.db.save(entry, callback);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Chain.prototype.resetLastCheckpoint = function resetLastCheckpoint(height) {
|
Chain.prototype.resetLastCheckpoint = function resetLastCheckpoint(height) {
|
||||||
@ -461,6 +673,12 @@ Chain.prototype.add = function add(block, peer, callback) {
|
|||||||
var hash, prevHash, prevHeight, entry, tip, existing, checkpoint;
|
var hash, prevHash, prevHeight, entry, tip, existing, checkpoint;
|
||||||
var total = 0;
|
var total = 0;
|
||||||
|
|
||||||
|
callback = utils.asyncify(callback);
|
||||||
|
|
||||||
|
if (this._locked)
|
||||||
|
return callback(null, total);
|
||||||
|
|
||||||
|
// (function next(block) {
|
||||||
(function next() {
|
(function next() {
|
||||||
hash = block.hash('hex');
|
hash = block.hash('hex');
|
||||||
prevHash = block.prevBlock;
|
prevHash = block.prevBlock;
|
||||||
@ -553,17 +771,19 @@ Chain.prototype.add = function add(block, peer, callback) {
|
|||||||
// The block has equal chainwork (an
|
// The block has equal chainwork (an
|
||||||
// alternate tip). Reset the chain, find
|
// alternate tip). Reset the chain, find
|
||||||
// a new peer, and wait to see who wins.
|
// a new peer, and wait to see who wins.
|
||||||
self.resetHeight(entry.height - 1);
|
self._locked = true;
|
||||||
self.emit('fork', {
|
|
||||||
height: prevHeight + 1,
|
|
||||||
expected: tip.hash,
|
|
||||||
received: hash,
|
|
||||||
checkpoint: false
|
|
||||||
}, peer);
|
|
||||||
code = Chain.codes.forked;
|
|
||||||
return self._removeBlock(tip.hash, function(err) {
|
return self._removeBlock(tip.hash, function(err) {
|
||||||
|
self._locked = false;
|
||||||
if (err)
|
if (err)
|
||||||
return done(err);
|
return done(err);
|
||||||
|
self.resetHeight(entry.height - 1);
|
||||||
|
self.emit('fork', {
|
||||||
|
height: prevHeight + 1,
|
||||||
|
expected: tip.hash,
|
||||||
|
received: hash,
|
||||||
|
checkpoint: false
|
||||||
|
}, peer);
|
||||||
|
code = Chain.codes.forked;
|
||||||
return done(null, code);
|
return done(null, code);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -595,14 +815,12 @@ Chain.prototype.add = function add(block, peer, callback) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Could fill here for contextual verification.
|
// self._verifyContext(block, prev, function(err, verified) {
|
||||||
// Also check isSpent here!
|
|
||||||
// self._fillCoins(block, function(err) {
|
|
||||||
|
|
||||||
// Do "contextual" verification on our block
|
// Do "contextual" verification on our block
|
||||||
// now that we're certain its previous
|
// now that we're certain its previous
|
||||||
// block is in the chain.
|
// block is in the chain.
|
||||||
if (!block.verifyContext()) {
|
if (!block.verifyContext(prev)) {
|
||||||
code = Chain.codes.invalid;
|
code = Chain.codes.invalid;
|
||||||
self.emit('invalid', {
|
self.emit('invalid', {
|
||||||
height: prevHeight + 1,
|
height: prevHeight + 1,
|
||||||
|
|||||||
@ -196,6 +196,9 @@ Mempool.prototype.addTX = function addTX(tx, peer, callback) {
|
|||||||
if (this.txs[hash])
|
if (this.txs[hash])
|
||||||
return callback(new Error('Already have TX.'));
|
return callback(new Error('Already have TX.'));
|
||||||
|
|
||||||
|
if (tx.isCoinbase())
|
||||||
|
return callback(new Error('What?'));
|
||||||
|
|
||||||
this._lockTX(tx);
|
this._lockTX(tx);
|
||||||
|
|
||||||
this.block.fillCoin(tx, function(err) {
|
this.block.fillCoin(tx, function(err) {
|
||||||
@ -260,15 +263,6 @@ Mempool.prototype.addTX = function addTX(tx, peer, callback) {
|
|||||||
|
|
||||||
for (i = 0; i < tx.inputs.length; i++) {
|
for (i = 0; i < tx.inputs.length; i++) {
|
||||||
input = tx.inputs[i];
|
input = tx.inputs[i];
|
||||||
if (input.output.spent) {
|
|
||||||
return callback(new Error('TX is spending old outputs.'));
|
|
||||||
peer.reject({
|
|
||||||
data: tx.hash(),
|
|
||||||
reason: 'old-outputs'
|
|
||||||
});
|
|
||||||
pool.setMisbehavior(peer, 100);
|
|
||||||
return callback(new Error('TX is spending old outputs.'));
|
|
||||||
}
|
|
||||||
dup = self.spent[input.prevout.hash + '/' + input.prevout.index];
|
dup = self.spent[input.prevout.hash + '/' + input.prevout.index];
|
||||||
if (dup) {
|
if (dup) {
|
||||||
// Replace-by-fee
|
// Replace-by-fee
|
||||||
@ -283,7 +277,6 @@ Mempool.prototype.addTX = function addTX(tx, peer, callback) {
|
|||||||
data: tx.hash(),
|
data: tx.hash(),
|
||||||
reason: 'double-spend'
|
reason: 'double-spend'
|
||||||
});
|
});
|
||||||
pool.setMisbehavior(peer, 100);
|
|
||||||
return callback(new Error('TX is double spending.'));
|
return callback(new Error('TX is double spending.'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -301,7 +294,7 @@ Mempool.prototype.addTX = function addTX(tx, peer, callback) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tx.verify(true)) {
|
if (!tx.verify(null, true)) {
|
||||||
return callback(new Error('TX did not verify.'));
|
return callback(new Error('TX did not verify.'));
|
||||||
peer.reject({
|
peer.reject({
|
||||||
data: tx.hash(),
|
data: tx.hash(),
|
||||||
@ -314,15 +307,12 @@ Mempool.prototype.addTX = function addTX(tx, peer, callback) {
|
|||||||
for (i = 0; i < tx.inputs.length; i++) {
|
for (i = 0; i < tx.inputs.length; i++) {
|
||||||
input = tx.inputs[i];
|
input = tx.inputs[i];
|
||||||
self.spent[input.prevout.hash + '/' + input.prevout.index] = tx;
|
self.spent[input.prevout.hash + '/' + input.prevout.index] = tx;
|
||||||
|
self.size += input.output.getSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Possibly do something bitcoinxt-like here with priority
|
// Possibly do something bitcoinxt-like here with priority
|
||||||
priority = tx.getPriority();
|
priority = tx.getPriority();
|
||||||
|
|
||||||
self.txs[hash] = tx;
|
|
||||||
self.count++;
|
|
||||||
self.size += tx.getSize();
|
|
||||||
|
|
||||||
tx.inputs.forEach(function(input) {
|
tx.inputs.forEach(function(input) {
|
||||||
var address = input.getAddress();
|
var address = input.getAddress();
|
||||||
|
|
||||||
@ -346,6 +336,10 @@ Mempool.prototype.addTX = function addTX(tx, peer, callback) {
|
|||||||
|
|
||||||
self.addresses[address][hash] = true;
|
self.addresses[address][hash] = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
self.txs[hash] = tx;
|
||||||
|
self.count++;
|
||||||
|
self.size += tx.getSize();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -402,11 +396,6 @@ Mempool.prototype.removeTX = function removeTX(hash, callback) {
|
|||||||
delete this.spent[id];
|
delete this.spent[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
delete this.txs[hash];
|
|
||||||
|
|
||||||
this.count--;
|
|
||||||
this.size -= tx.getSize();
|
|
||||||
|
|
||||||
tx.inputs.forEach(function(input) {
|
tx.inputs.forEach(function(input) {
|
||||||
var address = input.getAddress();
|
var address = input.getAddress();
|
||||||
|
|
||||||
@ -432,6 +421,10 @@ Mempool.prototype.removeTX = function removeTX(hash, callback) {
|
|||||||
delete self.addresses[address];
|
delete self.addresses[address];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
delete this.txs[hash];
|
||||||
|
this.count--;
|
||||||
|
this.size -= tx.getSize();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Need to lock the mempool when
|
// Need to lock the mempool when
|
||||||
|
|||||||
@ -918,6 +918,9 @@ TX.prototype.verify = function verify(index, force, flags) {
|
|||||||
if (index != null)
|
if (index != null)
|
||||||
assert(this.inputs[index]);
|
assert(this.inputs[index]);
|
||||||
|
|
||||||
|
if (this.isCoinbase())
|
||||||
|
return true;
|
||||||
|
|
||||||
return this.inputs.every(function(input, i) {
|
return this.inputs.every(function(input, i) {
|
||||||
if (index != null && i !== index)
|
if (index != null && i !== index)
|
||||||
return true;
|
return true;
|
||||||
@ -1552,6 +1555,10 @@ TX.prototype.isStandard = function isStandard() {
|
|||||||
if (bcoin.script.getSize(input.script) > 1650)
|
if (bcoin.script.getSize(input.script) > 1650)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// Not accurate?
|
||||||
|
if (this.isCoinbase())
|
||||||
|
continue;
|
||||||
|
|
||||||
if (!bcoin.script.isPushOnly(input.script))
|
if (!bcoin.script.isPushOnly(input.script))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1405,7 +1405,8 @@ utils.cmp = function(a, b) {
|
|||||||
// This protects us against timing attacks when
|
// This protects us against timing attacks when
|
||||||
// comparing an input against a secret string.
|
// comparing an input against a secret string.
|
||||||
utils.ccmp = function(a, b) {
|
utils.ccmp = function(a, b) {
|
||||||
var res, i;
|
var res = 0;
|
||||||
|
var i;
|
||||||
|
|
||||||
assert(a.length === b.length);
|
assert(a.length === b.length);
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user