work on chain. check for low der sigs.

This commit is contained in:
Christopher Jeffrey 2016-01-17 19:30:25 -08:00
parent 46b2983eb3
commit 575843acef
6 changed files with 118 additions and 54 deletions

View File

@ -223,26 +223,37 @@ Block.prototype.getMerkleRoot = function getMerkleRoot() {
return merkleTree[merkleTree.length - 1];
};
Block.prototype._debug = function debug() {
var args = Array.prototype.slice.call(arguments);
if (!this.chain)
return;
args.unshift('debug');
return this.chain.emit.apply(this.chain, args);
};
Block.prototype._verify = function _verify() {
var uniq = {};
var i, tx, hash;
// Check proof of work
if (!utils.testTarget(this.bits, this.hash())) {
this.chain.emit('debug', 'Block failed POW test: %s', this.rhash);
this._debug('Block failed POW test: %s', this.rhash);
return false;
}
// Check timestamp against now + 2 hours
if (this.ts > utils.now() + 2 * 60 * 60) {
this.chain.emit('debug', 'Block timestamp is too high: %s', this.rhash);
this._debug('Block timestamp is too high: %s', this.rhash);
return false;
}
// Verify the partial merkle tree if we are a merkleblock.
if (this.subtype === 'merkleblock') {
if (!this._verifyPartial()) {
this.chain.emit('debug', 'Block failed merkle test: %s', this.rhash);
this._debug('Block failed merkle test: %s', this.rhash);
return false;
}
}
@ -254,13 +265,13 @@ Block.prototype._verify = function _verify() {
// Size can't be bigger than MAX_BLOCK_SIZE
if (this.txs.length > constants.block.maxSize
|| this.size() > constants.block.maxSize) {
this.chain.emit('debug', 'Block is too large: %s', this.rhash);
this._debug('Block is too large: %s', this.rhash);
return false;
}
// First TX must be a coinbase
if (!this.txs.length || !this.txs[0].isCoinbase()) {
this.chain.emit('debug', 'Block has no coinbase: %s', this.rhash);
this._debug('Block has no coinbase: %s', this.rhash);
return false;
}
@ -270,14 +281,14 @@ Block.prototype._verify = function _verify() {
// The rest of the txs must not be coinbases
if (i > 0 && tx.isCoinbase()) {
this.chain.emit('debug', 'Block more than one coinbase: %s', this.rhash);
this._debug('Block more than one coinbase: %s', this.rhash);
return false;
}
// Check for duplicate txids
hash = tx.hash('hex');
if (uniq[hash]) {
this.chain.emit('debug', 'Block has duplicate txids: %s', this.rhash);
this._debug('Block has duplicate txids: %s', this.rhash);
return false;
}
uniq[hash] = true;
@ -285,14 +296,14 @@ Block.prototype._verify = function _verify() {
// Check merkle root
if (this.getMerkleRoot() !== this.merkleRoot) {
this.chain.emit('debug', 'Block failed merkleroot test: %s', this.rhash);
this._debug('Block failed merkleroot test: %s', this.rhash);
return false;
}
return true;
};
Block.prototype.postVerify = function postVerify() {
Block.prototype.verifyContext = function verifyContext() {
var flags = {};
var sigops = 0;
var prev, height, ts, i, j, tx, cb, input;
@ -310,7 +321,7 @@ Block.prototype.postVerify = function postVerify() {
// Ensure it's not an orphan
if (!prev) {
this.chain.emit('debug', 'Block has no previous entry: %s', this.rhash);
this._debug('Block has no previous entry: %s', this.rhash);
return false;
}
@ -318,41 +329,41 @@ Block.prototype.postVerify = function postVerify() {
// Ensure the timestamp is correct
if (this.ts <= prev.getMedianTime()) {
this.chain.emit('debug', 'Block time is lower than median: %s', this.rhash);
this._debug('Block time is lower than median: %s', this.rhash);
return false;
}
// Ensure the miner's target is equal to what we expect
if (this.bits !== this.chain.target(prev, this)) {
this.chain.emit('debug', 'Block is using wrong target: %s', this.rhash);
this._debug('Block is using wrong target: %s', this.rhash);
return false;
}
// Only allow version 2 blocks (coinbase height)
// once the majority of blocks are using it.
if (this.version < 2 && prev.isOutdated(2)) {
this.chain.emit('debug', 'Block is outdated (v2): %s', this.rhash);
this._debug('Block is outdated (v2): %s', this.rhash);
return false;
}
// Only allow version 3 blocks (sig validation)
// once the majority of blocks are using it.
if (this.version < 3 && prev.isOutdated(3)) {
this.chain.emit('debug', 'Block is outdated (v3): %s', this.rhash);
this._debug('Block is outdated (v3): %s', this.rhash);
return false;
}
// Only allow version 4 blocks (checklocktimeverify)
// once the majority of blocks are using it.
if (this.version < 4 && prev.isOutdated(4)) {
this.chain.emit('debug', 'Block is outdated (v4): %s', this.rhash);
this._debug('Block is outdated (v4): %s', this.rhash);
return false;
}
// Only allow version 8 blocks (locktime median past)
// once the majority of blocks are using it.
// if (this.version < 8 && prev.isOutdated(8)) {
// this.chain.emit('debug', 'Block is outdated (v8): %s', this.rhash);
// this._debug('Block is outdated (v8): %s', this.rhash);
// return false;
// }
@ -362,13 +373,13 @@ Block.prototype.postVerify = function postVerify() {
// Make sure the coinbase is parseable.
if (!cb) {
this.chain.emit('debug', 'Block has malformed coinbase: %s', this.rhash);
this._debug('Block has malformed coinbase: %s', this.rhash);
return false;
}
// Make sure coinbase height is equal to the actual height.
if (cb.height !== height) {
this.chain.emit('debug', 'Block has bad coinbase height: %s', this.rhash);
this._debug('Block has bad coinbase height: %s', this.rhash);
return false;
}
}
@ -406,7 +417,7 @@ Block.prototype.postVerify = function postVerify() {
// Transactions must be finalized with
// regards to nSequence and nLockTime.
if (!tx.isFinal(height, ts)) {
this.chain.emit('debug', 'TX is not final: %s (%s)', this.rhash, i);
this._debug('TX is not final: %s (%s)', this.rhash, i);
return false;
}
@ -416,7 +427,7 @@ Block.prototype.postVerify = function postVerify() {
// if (tx.sigops(true) > constants.script.maxTxSigops) {
// // Block 71036 abused checksig to
// // include a huge number of sigops.
// this.chain.emit('debug', 'Block TX has too many sigops: %s', this.rhash);
// this._debug('Block TX has too many sigops: %s', this.rhash);
// if (!(network.type === 'main' && height === 71036))
// return false;
// }
@ -430,7 +441,7 @@ Block.prototype.postVerify = function postVerify() {
sigops += tx.sigops();
if (sigops > constants.script.maxBlockSigops) {
this.chain.emit('debug', 'Block has too many sigops: %s', this.rhash);
this._debug('Block has too many sigops: %s', this.rhash);
return false;
}
@ -439,7 +450,7 @@ Block.prototype.postVerify = function postVerify() {
// Blocks 91842 and 91880 created duplicate
// txids by using the same exact output script
// and extraNonce.
this.chain.emit('debug', 'Block is overwriting txids: %s', this.rhash);
this._debug('Block is overwriting txids: %s', this.rhash);
if (!(network.type === 'main' && (height === 91842 || height === 91880)))
return false;
}
@ -461,13 +472,13 @@ Block.prototype.postVerify = function postVerify() {
// Verify the script
if (!tx.verify(j, true, flags)) {
this.chain.emit('debug', 'Block has invalid inputs: %s', this.rhash);
this._debug('Block has invalid inputs: %s', this.rhash);
return false;
}
// Ensure tx is not double spending an output
// if (this.chain.isSpent(input.out.hash, input.out.index)) {
// this.chain.emit('debug', 'Block is using spent inputs: %s', this.rhash);
// this._debug('Block is using spent inputs: %s', this.rhash);
// return false;
// }
}

View File

@ -179,12 +179,6 @@ Chain.prototype._addIndex = function _addIndex(entry, save) {
// could be used if you want to be on the overly
// safe (see: paranoid) side.
// this.resetLastCheckpoint(entry.height);
this.emit('fork', {
height: entry.height,
expected: checkpoint,
received: entry.hash,
checkpoint: true
});
return Chain.codes.badCheckpoint;
}
}
@ -294,18 +288,26 @@ Chain.prototype.add = function add(block, peer) {
var total = 0;
for (;;) {
hash = block.hash('hex');
prevHash = block.prevBlock;
// Find the previous block height/index.
prevHeight = this.index.heights[prevHash];
// Validate the block we want to add.
// This is only necessary for new
// blocks coming in, not the resolving
// orphans.
if (block === initial && !block.verify()) {
code = Chain.codes.invalid;
this.emit('invalid', {
height: prevHeight + 1,
hash: hash,
peer: peer
});
break;
}
hash = block.hash('hex');
prevHash = block.prevBlock;
// If the block is already known to be
// an orphan, ignore it.
if (this.orphan.map[prevHash]) {
@ -320,7 +322,8 @@ Chain.prototype.add = function add(block, peer) {
height: -1,
expected: this.orphan.map[prevHash].hash('hex'),
received: hash,
checkpoint: null
checkpoint: null,
peer: peer
});
code = Chain.codes.forked;
break;
@ -329,9 +332,6 @@ Chain.prototype.add = function add(block, peer) {
break;
}
// Find the previous block height/index.
prevHeight = this.index.heights[prevHash];
// If previous block wasn't ever seen,
// add it current to orphans and break.
if (prevHeight == null) {
@ -344,6 +344,7 @@ Chain.prototype.add = function add(block, peer) {
break;
}
// Create a new chain entry.
entry = new ChainBlock(this, {
hash: hash,
version: block.version,
@ -381,8 +382,9 @@ Chain.prototype.add = function add(block, peer) {
this.emit('fork', {
height: prevHeight + 1,
expected: tip.hash,
received: entry.hash,
checkpoint: null
received: hash,
checkpoint: null,
peer: peer
});
code = Chain.codes.forked;
break;
@ -391,10 +393,13 @@ Chain.prototype.add = function add(block, peer) {
// Do "contextual" verification on our block
// now that we're certain its previous
// block is in the chain.
// if (0)
if (!block.postVerify()) {
throw new Error;
if (!block.verifyContext()) {
code = Chain.codes.invalid;
this.emit('invalid', {
height: prevHeight + 1,
hash: hash,
peer: peer
});
break;
}
@ -412,6 +417,19 @@ Chain.prototype.add = function add(block, peer) {
// so we don't do it. The misbehaving peer has
// been killed and hopefully we find a peer
// who isn't trying to fool us.
if (code === Chain.codes.badCheckpoint) {
this.emit('fork', {
height: entry.height,
expected: network.checkpoints[entry.height],
received: entry.hash,
checkpoint: true,
peer: peer
});
break;
}
// Should never happen, but... something
// went wrong. Ignore this block.
if (code !== Chain.codes.okay)
break;

View File

@ -177,20 +177,35 @@ Pool.prototype._init = function _init() {
});
this.chain.on('fork', function(data) {
var peer = self.peers.load;
this.emit('debug',
'Fork at height %d: expected=%s received=%s checkpoint=%s',
self.emit('debug',
'Fork at height %d: expected=%s received=%s checkpoint=%s peer=%s',
data.height,
utils.revHex(data.expected),
utils.revHex(data.received),
data.checkpoint
data.checkpoint,
data.peer ? data.peer.host : ''
);
if (!peer)
if (!data.peer)
return;
peer.destroy();
data.peer.destroy();
});
this.chain.on('invalid', function(data) {
self.emit('debug',
'Invalid block at height: %d: hash=%s peer=%s',
data.height,
utils.revHex(data.hash),
data.peer ? data.peer.host : ''
);
if (!data.peer)
return;
// We should technically use a ban score
// here instead of killing the peer.
data.peer.destroy();
});
this.options.wallets.forEach(function(w) {

View File

@ -791,6 +791,8 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
if (flags.strictder !== false) {
if (!script.isValidSignature(sig))
return false;
if (!script.isLowDER(sig))
return false;
}
type = sig[sig.length - 1];
@ -860,6 +862,8 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
if (flags.strictder !== false) {
if (!script.isValidSignature(sig))
return false;
if (!script.isLowDER(sig))
return false;
}
type = sig[sig.length - 1];

View File

@ -1085,7 +1085,7 @@ TX.prototype.fill = function fill(txs) {
return inputs.length === this.inputs.length;
};
// Used for postVerify/ContextualBlockCheck and miner isFinalTx call.
// Used for verifyContext/ContextualBlockCheck and miner isFinalTx call.
// BIP113 will require that time-locked transactions have nLockTime set to
// less than the median time of the previous block they're contained in.
TX.prototype.isFinalBlock = function isFinalBlock(block, prev, useMedian) {
@ -1096,17 +1096,29 @@ TX.prototype.isFinalBlock = function isFinalBlock(block, prev, useMedian) {
// Used in AcceptToMemoryPool
TX.prototype.isFinalMempool = function isFinalMempool(useMedian) {
var height = this.chain.height() + 1;
var ts = useMedian
var height, ts;
if (!this.chain)
return true;
height = this.chain.height() + 1;
ts = useMedian
? this.chain.getTip().getMedianTime()
: utils.now();
return this.isFinal(height, ts);
};
// Used in the original bitcoind code for AcceptBlock
TX.prototype.isFinalLegacy = function isFinalLegacy(block) {
var ts = block ? block.ts : utils.now();
var height = this.chain.height();
var ts, height;
if (!this.chain)
return true;
ts = block ? block.ts : utils.now();
height = this.chain.height();
return this.isFinal(height, ts);
};

View File

@ -22,6 +22,10 @@ pool.on('error', function(err) {
utils.print('Error: %s', err.message);
});
pool.on('debug', function() {
utils.print.apply(utils, arguments);
});
console.log('Updating bcoin preloaded chain...');
pool.on('block', function(block) {