diff --git a/lib/chain/chain.js b/lib/chain/chain.js index f23988ae..ad102dbe 100644 --- a/lib/chain/chain.js +++ b/lib/chain/chain.js @@ -285,13 +285,27 @@ Chain.prototype.isGenesis = function isGenesis(block) { Chain.prototype.verify = co(function* verify(block, prev) { var ret = new VerifyResult(); - var i, height, ts, tx, medianTime, commitmentHash, ancestors, state; + var i, err, height, ts, tx, medianTime; + var commitmentHash, ancestors, state; if (!block.verify(ret)) { - throw new VerifyError(block, + err = new VerifyError(block, 'invalid', ret.reason, ret.score); + + // High hash is the only thing an + // adversary couldn't mutate in + // otherwise valid non-contextual + // checks. The block timestamp + // can't be mutated, but our + // adjusted time might be off. + // We may be able to accept the + // block later. + if (ret.reason !== 'high-hash') + err.malleated = true; + + throw err; } // Skip the genesis block. Skip all blocks in spv mode. @@ -349,17 +363,28 @@ Chain.prototype.verify = co(function* verify(block, prev) { if (state.hasWitness()) { commitmentHash = block.commitmentHash; if (commitmentHash) { + // These are totally malleable. Someone + // may have even accidentally sent us + // the non-witness version of the block. + // We don't want to consider this block + // "invalid" if either of these checks + // fail. if (!block.witnessNonce) { - throw new VerifyError(block, + err = new VerifyError(block, 'invalid', 'bad-witness-merkle-size', 100); + err.malleated = true; + throw err; } + if (commitmentHash !== block.getCommitmentHash('hex')) { - throw new VerifyError(block, + err = new VerifyError(block, 'invalid', 'bad-witness-merkle-match', 100); + err.malleated = true; + throw err; } } } @@ -368,10 +393,12 @@ Chain.prototype.verify = co(function* verify(block, prev) { // witness data cannot contain it. if (!commitmentHash) { if (block.hasWitness()) { - throw new VerifyError(block, + err = new VerifyError(block, 'invalid', 'unexpected-witness', 100); + err.malleated = true; + throw err; } } @@ -842,7 +869,8 @@ Chain.prototype.reconnect = co(function* reconnect(entry) { view = yield this.verifyContext(block, prev); } catch (e) { if (e.type === 'VerifyError') { - this.invalid[entry.hash] = true; + if (!e.malleated) + this.invalid[entry.hash] = true; this.emit('invalid', block, entry.height); } throw e; @@ -897,7 +925,8 @@ Chain.prototype.setBestChain = co(function* setBestChain(entry, block, prev) { block.setHeight(-1); if (e.type === 'VerifyError') { - this.invalid[entry.hash] = true; + if (!e.malleated) + this.invalid[entry.hash] = true; this.emit('invalid', block, entry.height); } @@ -1082,7 +1111,8 @@ Chain.prototype._add = co(function* add(block) { // blocks coming in, not the resolving // orphans. if (initial && !block.verify(ret)) { - this.invalid[hash] = true; + if (ret.reason === 'high-hash') + this.invalid[hash] = true; this.emit('invalid', block, block.getCoinbaseHeight()); throw new VerifyError(block, 'invalid', ret.reason, ret.score); }