more chain refactoring.

This commit is contained in:
Christopher Jeffrey 2016-03-30 17:32:39 -07:00
parent 4d2d9b328c
commit 9b4e363a80
7 changed files with 299 additions and 268 deletions

View File

@ -75,7 +75,6 @@ AbstractBlock.prototype.verifyHeaders = function verifyHeaders(ret) {
// Check proof of work
if (!utils.testTarget(this.bits, this.hash())) {
utils.debug('Block failed POW test: %s', this.rhash);
ret.reason = 'high-hash';
ret.score = 50;
return false;
@ -83,7 +82,6 @@ AbstractBlock.prototype.verifyHeaders = function verifyHeaders(ret) {
// Check timestamp against now + 2 hours
if (this.ts > utils.now() + 2 * 60 * 60) {
utils.debug('Block timestamp is too high: %s', this.rhash);
ret.reason = 'time-too-new';
ret.score = 0;
return false;

View File

@ -181,7 +181,6 @@ Block.prototype._verify = function _verify(ret) {
// Size can't be bigger than MAX_BLOCK_SIZE
if (this.txs.length > constants.block.maxSize
|| this.getVirtualSize() > constants.block.maxSize) {
utils.debug('Block is too large: %s', this.rhash);
ret.reason = 'bad-blk-length';
ret.score = 100;
return false;
@ -189,7 +188,6 @@ Block.prototype._verify = function _verify(ret) {
// First TX must be a coinbase
if (!this.txs.length || !this.txs[0].isCoinbase()) {
utils.debug('Block has no coinbase: %s', this.rhash);
ret.reason = 'bad-cb-missing';
ret.score = 100;
return false;
@ -201,7 +199,6 @@ Block.prototype._verify = function _verify(ret) {
// The rest of the txs must not be coinbases
if (i > 0 && tx.isCoinbase()) {
utils.debug('Block more than one coinbase: %s', this.rhash);
ret.reason = 'bad-cb-multiple';
ret.score = 100;
return false;
@ -210,7 +207,6 @@ Block.prototype._verify = function _verify(ret) {
// Check for duplicate txids
hash = tx.hash('hex');
if (uniq[hash]) {
utils.debug('Block has duplicate txids: %s', this.rhash);
ret.reason = 'bad-txns-duplicate';
ret.score = 100;
return false;
@ -220,7 +216,6 @@ Block.prototype._verify = function _verify(ret) {
// Check merkle root
if (this.getMerkleRoot() !== this.merkleRoot) {
utils.debug('Block failed merkleroot test: %s', this.rhash);
ret.reason = 'bad-txnmrkleroot';
ret.score = 100;
return false;

View File

@ -62,25 +62,28 @@ utils.inherits(Chain, EventEmitter);
Chain.prototype._init = function _init() {
var self = this;
function getPeer() {
function getHost() {
var peer;
if (!self.node || !self.node.pool)
return;
return self.node.pool.peers.load;
}
peer = self.node.pool.peers.load;
function getHost() {
var peer = getPeer();
if (peer)
return peer.host;
return 'unknown';
if (!peer)
return 'unknown';
return peer.host;
}
// Hook into events for debugging
// this.on('block', function(block, entry) {
// utils.debug('Block %s (%d) added to chain (%s)',
// utils.revHex(entry.hash), entry.height, getHost());
// });
this.on('block', function(block, entry) {
if (self.height < 400000)
return;
utils.debug('Block %s (%d) added to chain (%s)',
utils.revHex(entry.hash), entry.height, getHost());
});
this.on('competitor', function(block, entry) {
utils.debug('Heads up: Competing chain at height %d:'
@ -397,8 +400,9 @@ Chain.prototype._verifyContext = function _verifyContext(block, prev, callback)
Chain.prototype._verify = function _verify(block, prev, callback) {
var self = this;
var flags = constants.flags.MANDATORY_VERIFY_FLAGS;
var lockFlags = constants.flags.MANDATORY_LOCKTIME_FLAGS;
var height, ts, i, tx, coinbaseHeight;
var medianTime, locktimeMedian, segwit;
var medianTime, segwit;
var ret = {};
function done(err, result) {
@ -414,10 +418,8 @@ Chain.prototype._verify = function _verify(block, prev, callback) {
return done(null, flags);
// Ensure it's not an orphan
if (!prev) {
utils.debug('Block has no previous entry: %s', block.rhash);
if (!prev)
return done(new VerifyError(block, 'invalid', 'bad-prevblk', 0));
}
prev.ensureAncestors(function(err) {
if (err)
@ -427,15 +429,11 @@ Chain.prototype._verify = function _verify(block, prev, callback) {
medianTime = prev.getMedianTime();
// Ensure the timestamp is correct
if (block.ts <= medianTime) {
utils.debug('Block time is lower than median: %s', block.rhash);
if (block.ts <= medianTime)
return done(new VerifyError(block, 'invalid', 'time-too-old', 0));
}
if (block.bits !== self.getTarget(prev, block)) {
utils.debug('Block is using wrong target: %s', block.rhash);
if (block.bits !== self.getTarget(prev, block))
return done(new VerifyError(block, 'invalid', 'bad-diffbits', 100));
}
// For some reason bitcoind has p2sh in the
// mandatory flags by default, when in reality
@ -443,47 +441,37 @@ Chain.prototype._verify = function _verify(block, prev, callback) {
// The first p2sh output and redeem script
// appeared on march 7th 2012, only it did
// not have a signature. See:
// https://blockchain.info/tx/6a26d2ecb67f27d1fa5524763b49029d7106e91e3cc05743073461a719776192
// https://blockchain.info/tx/9c08a4d78931342b37fd5f72900fb9983087e6f46c4a097d8a1f52c74e28eaf6
// 6a26d2ecb67f27d1fa5524763b49029d7106e91e3cc05743073461a719776192
// 9c08a4d78931342b37fd5f72900fb9983087e6f46c4a097d8a1f52c74e28eaf6
if (block.ts < constants.block.bip16time)
flags &= ~constants.flags.VERIFY_P2SH;
// 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);
if (block.version < 2 && prev.isOutdated(2))
return done(new VerifyError(block, 'obsolete', 'bad-version', 0));
}
// 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);
if (block.version < 3 && prev.isOutdated(3))
return done(new VerifyError(block, 'obsolete', 'bad-version', 0));
}
// 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);
if (block.version < 4 && prev.isOutdated(4))
return done(new VerifyError(block, 'obsolete', 'bad-version', 0));
}
// Only allow version 5 blocks (segwit)
// once the majority of blocks are using it.
if (network.segwitHeight !== -1 && height >= network.segwitHeight) {
if (block.version < 5 && prev.isOutdated(5)) {
utils.debug('Block is outdated (v5): %s', block.rhash);
if (block.version < 5 && prev.isOutdated(5))
return done(new VerifyError(block, 'obsolete', 'bad-version', 0));
}
}
// 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);
// }
// if (block.version < 8 && prev.isOutdated(8))
// return done(new VerifyError(block, 'obsolete', 'bad-version', 0));
// Make sure the height contained in the coinbase is correct.
if (network.block.bip34height !== -1 && height >= network.block.bip34height) {
@ -499,7 +487,7 @@ Chain.prototype._verify = function _verify(block, prev, callback) {
if (block.version >= 4 && prev.isUpgraded(4))
flags |= constants.flags.VERIFY_CHECKLOCKTIMEVERIFY;
// Segregrated witness is now usable (the-bip-that-really-needs-to-be-rewritten)
// Segregrated witness is now usable
if (network.segwitHeight !== -1 && height >= network.segwitHeight) {
if (block.version >= 5 && prev.isUpgraded(5)) {
flags |= constants.flags.VERIFY_WITNESS;
@ -510,32 +498,40 @@ Chain.prototype._verify = function _verify(block, prev, callback) {
}
}
// Locktime median time past is now enforced.
// if (block.version >= 8 && prev.isUpgraded(8))
// lockFlags |= constants.flags.MEDIAN_TIME_PAST;
// Can't verify any further when merkleblock or headers.
if (block.type !== 'block')
return done(null, flags);
// Make sure the height contained in the coinbase is correct.
if (coinbaseHeight) {
if (block.getCoinbaseHeight() !== height) {
utils.debug('Block has bad coinbase height: %s', block.rhash);
if (block.getCoinbaseHeight() !== height)
return done(new VerifyError(block, 'invalid', 'bad-cb-height', 100));
}
}
if (block.version >= 5 && segwit) {
if (block.commitmentHash !== block.getCommitmentHash()) {
utils.debug('Block failed witnessroot test: %s', block.rhash);
return done(new VerifyError(block, 'invalid', 'bad-blk-wit-length', 100));
return done(new VerifyError(block,
'invalid',
'bad-blk-wit-length',
100));
}
} else {
if (block.hasWitness()) {
utils.debug('Unexpected witness data found: %s', block.rhash);
return done(new VerifyError(block, 'invalid', 'unexpected-witness', 100));
return done(new VerifyError(block,
'invalid',
'unexpected-witness',
100));
}
}
// Get timestamp for tx.isFinal().
ts = locktimeMedian ? medianTime : block.ts;
ts = (lockFlags & constants.flags.MEDIAN_TIME_PAST)
? medianTime
: block.ts;
// Check all transactions
for (i = 0; i < block.txs.length; i++) {
@ -544,8 +540,10 @@ Chain.prototype._verify = function _verify(block, prev, callback) {
// 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 done(new VerifyError(block, 'invalid', 'bad-txns-nonfinal', 10));
return done(new VerifyError(block,
'invalid',
'bad-txns-nonfinal',
10));
}
}
@ -572,13 +570,14 @@ Chain.prototype._checkDuplicates = function _checkDuplicates(block, prev, callba
if (err)
return next(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 next();
// Blocks 91842 and 91880 created duplicate
// txids by using the same exact output script
// and extraNonce.
if (network.type === 'main') {
if (height === 91842 || height === 91880)
return next();
}
return next(new VerifyError(block, 'invalid', 'bad-txns-BIP30', 100));
}
@ -588,6 +587,7 @@ Chain.prototype._checkDuplicates = function _checkDuplicates(block, prev, callba
};
Chain.prototype._checkInputs = function _checkInputs(block, prev, flags, callback) {
var self = this;
var height = prev.height + 1;
var scriptCheck = true;
var historical = false;
@ -631,25 +631,28 @@ Chain.prototype._checkInputs = function _checkInputs(block, prev, flags, callbac
sigops += tx.getSigops();
if (sigops > constants.block.maxSigops) {
utils.debug('Block has too many sigops: %s', block.rhash);
return callback(new VerifyError(block, 'invalid', 'bad-blk-sigops', 100));
return callback(new VerifyError(block,
'invalid',
'bad-blk-sigops',
100));
}
// Coinbases do not have prevouts
if (tx.isCoinbase())
continue;
if (!tx.checkInputs(height, ret))
return callback(new VerifyError(block, 'invalid', ret.reason, ret.score));
if (!tx.checkInputs(height, ret)) {
return callback(new VerifyError(block,
'invalid',
ret.reason,
ret.score));
}
for (j = 0; j < tx.inputs.length; j++) {
input = tx.inputs[j];
// Ensure tx is not double spending an output
if (!input.coin) {
utils.debug('Block is using spent inputs: %s (tx: %s, output: %s)',
block.rhash, tx.rhash,
utils.revHex(input.prevout.hash) + '/' + input.prevout.index);
assert(!historical, 'BUG: Spent inputs in historical data!');
return callback(new VerifyError(block,
'invalid',
@ -665,17 +668,6 @@ Chain.prototype._checkInputs = function _checkInputs(block, prev, flags, callbac
// Verify the scripts
if (!tx.verify(j, true, flags)) {
utils.debug('Block has invalid inputs: %s (%s/%d)',
block.rhash, tx.rhash, j);
utils.debug(input);
utils.debug('Signature Hash v0: %s',
utils.toHex(tx.signatureHash(j, input.coin.script, 'all', 0)));
utils.debug('Signature Hash v1: %s',
utils.toHex(tx.signatureHash(j, input.coin.script, 'all', 1)));
utils.debug('Raw Script: %s',
utils.toHex(input.coin.script.encode()));
utils.debug('Reserialized Script: %s',
utils.toHex(input.coin.script.clone().encode()));
assert(!historical, 'BUG: Invalid inputs in historical data!');
return callback(new VerifyError(block,
'invalid',
@ -699,7 +691,6 @@ Chain.prototype._checkInputs = function _checkInputs(block, prev, flags, callbac
return callback(err);
if (!verified) {
utils.debug('Block has invalid inputs: %s', block.rhash);
assert(!historical, 'BUG: Invalid inputs in historical data!');
return callback(new VerifyError(block,
'invalid',
@ -1011,17 +1002,74 @@ Chain.prototype.add = function add(block, callback, force) {
chain: !!self.invalid[prevHash]
});
self.invalid[hash] = true;
return done(new VerifyError(block, 'duplicate', 'duplicate', 0));
return done(new VerifyError(block, 'duplicate', 'duplicate', 100));
}
// Do we already have this block?
if (self.hasPending(hash)) {
self.emit('exists', block, {
height: block.getCoinbaseHeight(),
hash: hash
});
return done(new VerifyError(block, 'duplicate', 'duplicate', 0));
}
// If the block is already known to be
// an orphan, ignore it.
orphan = self.orphan.map[prevHash];
if (orphan) {
// If the orphan chain forked, simply
// reset the orphans.
if (orphan.hash('hex') !== hash) {
self.purgeOrphans();
self.purgePending();
self.emit('fork', block, {
height: block.getCoinbaseHeight(),
expected: orphan.hash('hex'),
received: hash,
checkpoint: false
});
return done(new VerifyError(block, 'duplicate', 'duplicate', 0));
}
self.emit('orphan', block, {
height: block.getCoinbaseHeight(),
hash: hash,
seen: true
});
return done(new VerifyError(block, 'invalid', 'bad-prevblk', 0));
}
// Special case for genesis block.
if (block.isGenesis())
return done();
// Validate the block we want to add.
// This is only necessary for new
// blocks coming in, not the resolving
// orphans.
if (initial && !block.verify(ret)) {
self.invalid[hash] = true;
self.emit('invalid', block, {
height: block.getCoinbaseHeight(),
hash: hash,
seen: false,
chain: false
});
return done(new VerifyError(block, 'invalid', ret.reason, ret.score));
}
self.db.has(hash, function(err, existing) {
if (err)
return done(err);
if (existing || self.hasPending(hash)) {
// Do we already have this block?
if (existing) {
self.emit('exists', block, {
height: -1,
height: block.getCoinbaseHeight(),
hash: hash
});
return done(new VerifyError(block, 'duplicate', 'duplicate', 0));
@ -1034,54 +1082,6 @@ Chain.prototype.add = function add(block, callback, force) {
height = !prev ? -1 : prev.height + 1;
// Validate the block we want to add.
// This is only necessary for new
// blocks coming in, not the resolving
// orphans.
if (initial && !block.verify(ret)) {
self.invalid[hash] = true;
self.emit('invalid', block, {
height: height,
hash: hash,
seen: false,
chain: false
});
return done(new VerifyError(block, 'invalid', ret.reason, ret.score));
}
// Special case for genesis block.
if (block.isGenesis())
return done();
// If the block is already known to be
// an orphan, ignore it.
orphan = self.orphan.map[prevHash];
if (orphan) {
// If the orphan chain forked, simply
// reset the orphans.
if (orphan.hash('hex') !== hash) {
self.purgeOrphans();
self.purgePending();
self.emit('fork', block, {
height: block.getCoinbaseHeight(),
expected: orphan.hash('hex'),
received: hash,
checkpoint: false
});
return done();
}
self.emit('orphan', block, {
height: block.getCoinbaseHeight(),
hash: hash,
seen: true
});
return done(new VerifyError(block, 'invalid', 'bad-prevblk', 0));
}
// Update the best height based on the coinbase.
// We do this even for orphans (peers will send
// us their highest block during the initial
@ -1590,13 +1590,13 @@ Chain.prototype.getLocator = function getLocator(start, callback, force) {
if (typeof height === 'string')
return next();
self.db.get(height, function(err, existing) {
self.db.getHash(height, function(err, hash) {
if (err)
return next(err);
assert(existing);
assert(hash);
hashes[i] = existing.hash;
hashes[i] = hash;
next();
});
@ -1949,6 +1949,101 @@ Chain.prototype.isSegwitActive = function isSegwitActive(callback) {
});
};
Chain.prototype.checkFinal = function checkFinal(prev, tx, flags, callback) {
var height = prev.height + 1;
function check(err, ts) {
if (err)
return callback(err);
return callback(null, tx.isFinal(ts, height));
}
if (flags & constants.flags.MEDIAN_TIME_PAST)
return prev.getMedianTimeAsync(check);
utils.asyncify(check)(null, utils.now());
};
Chain.prototype.getLocks = function getLocks(tx, flags, entry, callback) {
var self = this;
var mask = constants.sequenceLocktimeMask;
var granularity = constants.sequenceLocktimeGranularity;
var disableFlag = constants.sequenceLocktimeDisableFlag;
var typeFlag = constants.sequenceLocktimeTypeFlag;
var hasFlag = flags & constants.flags.VERIFY_SEQUENCE;
var minHeight = -1;
var minTime = -1;
var coinHeight;
if (tx.version < 2 || !hasFlag)
return utils.asyncify(callback)(null, minHeight, minTime);
utils.forEachSerial(tx.inputs, function(input, next) {
if (input.sequence & disableFlag)
return next();
coinHeight = input.coin.height === -1
? self.chain.tip + 1
: input.coin.height;
if ((input.sequence & typeFlag) === 0) {
coinHeight += (input.sequence & mask) - 1;
minHeight = Math.max(minHeight, coinHeight);
return next();
}
entry.getAncestorByHeight(Math.max(coinHeight - 1, 0), function(err, entry) {
if (err)
return next(err);
assert(entry, 'Database is corrupt.');
entry.getMedianTimeAsync(function(err, coinTime) {
if (err)
return next(err);
coinTime += ((input.sequence & mask) << granularity) - 1;
minTime = Math.max(minTime, coinTime);
next();
});
});
}, function(err) {
if (err)
return callback(err);
return callback(null, minHeight, minTime);
});
};
Chain.prototype.evalLocks = function evalLocks(entry, minHeight, minTime, callback) {
if (minHeight >= entry.height)
return utils.asyncify(callback)(null, false);
if (minTime === -1)
return utils.asyncify(callback)(null, true);
entry.getMedianTimeAsync(function(err, medianTime) {
if (err)
return callback(err);
if (minTime >= medianTime)
return callback(null, false);
return callback(null, true);
});
};
Chain.prototype.checkLocks = function checkLocks(tx, flags, entry, callback) {
var self = this;
this.getLocks(tx, flags, entry, function(err, minHeight, minTime) {
if (err)
return callback(err);
self.evalLocks(entry, minHeight, minTime, callback);
});
};
/**
* Expose
*/

View File

@ -61,6 +61,7 @@ utils.inherits(Mempool, EventEmitter);
Mempool.flags = constants.flags.STANDARD_VERIFY_FLAGS;
Mempool.mandatory = constants.flags.MANDATORY_VERIFY_FLAGS;
Mempool.lockFlags = constants.flags.STANDARD_LOCKTIME_FLAGS;
Mempool.ANCESTOR_LIMIT = 25;
Mempool.MAX_MEMPOOL_SIZE = 300 << 20;
@ -109,9 +110,13 @@ Mempool.prototype._init = function _init() {
else
self.size = size;
unlock();
self.loaded = true;
self.emit('open');
self.chain.open(function(err) {
if (err)
self.emit('error', err);
unlock();
self.loaded = true;
self.emit('open');
});
});
});
});
@ -285,9 +290,10 @@ Mempool.prototype.hasTX = function hasTX(hash, callback) {
Mempool.prototype.add =
Mempool.prototype.addTX = function addTX(tx, callback, force) {
var self = this;
var hash, ts, height, now;
var flags = Mempool.flags;
var lockFlags = Mempool.lockFlags;
var ret = {};
var now;
var unlock = this._lock(addTX, [tx, callback], force);
if (!unlock)
@ -298,8 +304,6 @@ Mempool.prototype.addTX = function addTX(tx, callback, force) {
flags |= constants.flags.VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM;
}
hash = tx.hash('hex');
callback = utils.wrap(callback, unlock);
callback = utils.asyncify(callback);
@ -321,70 +325,72 @@ Mempool.prototype.addTX = function addTX(tx, callback, force) {
if (tx.isCoinbase())
return callback(new VerifyError(tx, 'invalid', 'coinbase', 100));
// ts = locktimeFlags & LOCKTIME_MEDIAN_PAST
// ? self.chain.tip.getMedianTime()
// : utils.now();
ts = utils.now();
height = this.chain.height + 1;
if (!tx.isFinal(ts, height))
return callback(new VerifyError(tx, 'nonstandard', 'non-final', 0));
if (this.requireStandard) {
if (!tx.isStandard(flags, ret))
return callback(new VerifyError(tx, ret.reason, 0));
}
this._hasTX(tx, function(err, exists) {
self.chain.checkFinal(self.chain.tip, tx, lockFlags, function(err, isFinal) {
if (err)
return callback(err);
if (exists)
return callback(new VerifyError(tx, 'alreadyknown', 'txn-already-in-mempool', 0));
if (!isFinal)
return callback(new VerifyError(tx, 'nonstandard', 'non-final', 0));
self.tx.isDoubleSpend(tx, function(err, doubleSpend) {
if (self.requireStandard) {
if (!tx.isStandard(flags, ret))
return callback(new VerifyError(tx, ret.reason, 0));
}
self._hasTX(tx, function(err, exists) {
if (err)
return callback(err);
if (doubleSpend) {
if (exists) {
return callback(new VerifyError(tx,
'duplicate',
'bad-txns-inputs-spent',
'alreadyknown',
'txn-already-in-mempool',
0));
}
self.node.fillCoins(tx, function(err) {
self.tx.isDoubleSpend(tx, function(err, doubleSpend) {
if (err)
return callback(err);
if (!tx.hasCoins()) {
if (self.size > Mempool.MAX_MEMPOOL_SIZE) {
return callback(new VerifyError(tx,
'insufficientfee',
'mempool full',
0));
}
utils.debug('Added orphan %s to mempool.', tx.rhash);
return self.storeOrphan(tx, callback);
if (doubleSpend) {
return callback(new VerifyError(tx,
'duplicate',
'bad-txns-inputs-spent',
0));
}
self.verify(tx, function(err) {
self.node.fillCoins(tx, function(err) {
if (err)
return callback(err);
self.limitMempoolSize(function(err, result) {
if (err)
return callback(err);
if (!result) {
if (!tx.hasCoins()) {
if (self.size > Mempool.MAX_MEMPOOL_SIZE) {
return callback(new VerifyError(tx,
'insufficientfee',
'mempool full',
0));
}
utils.debug('Added orphan %s to mempool.', tx.rhash);
return self.storeOrphan(tx, callback);
}
self.addUnchecked(tx, callback);
self.verify(tx, function(err) {
if (err)
return callback(err);
self.limitMempoolSize(function(err, result) {
if (err)
return callback(err);
if (!result) {
return callback(new VerifyError(tx,
'insufficientfee',
'mempool full',
0));
}
self.addUnchecked(tx, callback);
});
});
});
});
@ -437,6 +443,7 @@ Mempool.prototype.removeUnchecked = function removeUnchecked(tx, callback) {
Mempool.prototype.verify = function verify(tx, callback) {
var self = this;
var height = this.chain.height + 1;
var lockFlags = Mempool.lockFlags;
var flags = Mempool.flags;
var mandatory = Mempool.mandatory;
var ret = {};
@ -448,7 +455,7 @@ Mempool.prototype.verify = function verify(tx, callback) {
mandatory |= constants.flags.VERIFY_WITNESS;
}
this.checkMempoolLocks(tx, flags, function(err, result) {
this.checkLocks(tx, lockFlags, function(err, result) {
if (err)
return callback(err);
@ -756,86 +763,7 @@ Mempool.prototype.getSnapshot = function getSnapshot(callback) {
return this.tx.getAllHashes(callback);
};
Mempool.prototype.getLocks = function getLocks(tx, flags, entry, callback) {
var self = this;
var mask = constants.sequenceLocktimeMask
var granularity = constants.sequenceLocktimeGranularity;
var disableFlag = constants.sequenceLocktimeDisableFlag;
var typeFlag = constants.sequenceLocktimeTypeFlag;
var hasFlag = flags & constants.flags.VERIFY_CHECKSEQUENCEVERIFY;
var minHeight = -1;
var minTime = -1;
var coinHeight;
if (tx.version < 2 || !hasFlag)
return utils.asyncify(callback)(null, minHeight, minTime);
utils.forEachSerial(tx.inputs, function(input, next) {
if (input.sequence & disableFlag)
return next();
coinHeight = input.coin.height === -1
? self.chain.tip + 1
: input.coin.height;
if ((input.sequence & typeFlag) === 0) {
coinHeight += (input.sequence & mask) - 1;
minHeight = Math.max(minHeight, coinHeight);
return next();
}
entry.getAncestorByHeight(Math.max(coinHeight - 1, 0), function(err, entry) {
if (err)
return next(err);
assert(entry, 'Database is corrupt.');
entry.getMedianTimeAsync(function(err, coinTime) {
if (err)
return next(err);
coinTime += ((input.sequence & mask) << granularity) - 1;
minTime = Math.max(minTime, coinTime);
next();
});
});
}, function(err) {
if (err)
return callback(err);
return callback(null, minHeight, minTime);
});
};
Mempool.prototype.evalLocks = function evalLocks(entry, minHeight, minTime, callback) {
if (minHeight >= entry.height)
return utils.asyncify(callback)(null, false);
if (minTime === -1)
return utils.asyncify(callback)(null, true);
entry.getMedianTimeAsync(function(err, medianTime) {
if (err)
return callback(err);
if (minTime >= medianTime)
return callback(null, false);
return callback(null, true);
});
};
Mempool.prototype.checkLocks = function checkLocks(tx, flags, entry, callback) {
var self = this;
this.getLocks(tx, flags, entry, function(err, minHeight, minTime) {
if (err)
return callback(err);
self.evalLocks(entry, minHeight, minTime, callback);
});
};
Mempool.prototype.checkMempoolLocks = function checkMempoolLocks(tx, flags, callback) {
Mempool.prototype.checkLocks = function checkLocks(tx, flags, callback) {
var self = this;
var tip = this.chain.tip;
@ -851,7 +779,7 @@ Mempool.prototype.checkMempoolLocks = function checkMempoolLocks(tx, flags, call
chainwork: tip.chainwork
});
this.checkLocks(tx, flags, index, callback);
return this.chain.checkLocks(tx, flags, index, callback);
};
/**

View File

@ -133,7 +133,6 @@ MerkleBlock.prototype._verify = function _verify(ret) {
// Verify the partial merkle tree if we are a merkleblock.
if (!this._verifyPartial()) {
utils.debug('Block failed merkle test: %s', this.rhash);
ret.reason = 'bad-txnmrklroot';
ret.score = 100;
return false;

View File

@ -294,7 +294,10 @@ exports.flags = {
VERIFY_WITNESS: (1 << 10),
VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM: (1 << 11),
// NOTE: Should be (1 << 10) - but that conflicts with segwit
VERIFY_CHECKSEQUENCEVERIFY: (1 << 12)
VERIFY_CHECKSEQUENCEVERIFY: (1 << 12),
VERIFY_SEQUENCE: (1 << 0),
MEDIAN_TIME_PAST: (1 << 1)
};
// Block validation
@ -310,10 +313,16 @@ exports.flags.STANDARD_VERIFY_FLAGS =
| exports.flags.VERIFY_DISCOURAGE_UPGRADABLE_NOPS
| exports.flags.VERIFY_CLEANSTACK
| exports.flags.VERIFY_CHECKLOCKTIMEVERIFY
| exports.flags.VERIFY_LOW_S;
| exports.flags.VERIFY_LOW_S
| exports.flags.VERIFY_CHECKSEQUENCEVERIFY;
// | exports.flags.VERIFY_WITNESS
// | exports.flags.VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM
// | exports.flags.VERIFY_CHECKSEQUENCEVERIFY;
exports.flags.MANDATORY_LOCKTIME_FLAGS = 0;
exports.flags.STANDARD_LOCKTIME_FLAGS =
exports.flags.VERIFY_SEQUENCE
| exports.flags.MEDIAN_TIME_PAST;
exports.versionbits = {
// What block version to use for new blocks (pre versionbits)

View File

@ -1997,11 +1997,18 @@ if (utils.isBrowser) {
function VerifyError(object, code, reason, score) {
Error.call(this);
if (Error.captureStackTrace)
Error.captureStackTrace(this, VerifyError);
this.type = 'VerifyError';
this.hash = object.hash();
this.height = object.height;
if (object.getCoinbaseHeight && this.height === -1)
this.height = object.getCoinbaseHeight();
this.code = code;
this.reason = score === -1 ? null : reason;
this.score = score;