block/chain: refactor block verification and caching.
This commit is contained in:
parent
179caa2bd8
commit
7688d80e74
@ -224,14 +224,15 @@ Chain.prototype._close = function close() {
|
||||
* @private
|
||||
* @param {Block} block
|
||||
* @param {ChainEntry} prev
|
||||
* @param {Number} flags
|
||||
* @returns {Promise} - Returns {@link ContextResult}.
|
||||
*/
|
||||
|
||||
Chain.prototype.verifyContext = co(function* verifyContext(block, prev) {
|
||||
Chain.prototype.verifyContext = co(function* verifyContext(block, prev, flags) {
|
||||
var state, view;
|
||||
|
||||
// Initial non-contextual verification.
|
||||
state = yield this.verify(block, prev);
|
||||
state = yield this.verify(block, prev, flags);
|
||||
|
||||
// BIP30 - Verify there are no duplicate txids.
|
||||
yield this.verifyDuplicates(block, prev, state);
|
||||
@ -269,20 +270,14 @@ Chain.prototype.verifyBlock = co(function* verifyBlock(block) {
|
||||
*/
|
||||
|
||||
Chain.prototype._verifyBlock = co(function* verifyBlock(block) {
|
||||
var valid = block._validHeaders;
|
||||
var result;
|
||||
var flags = common.flags.DEFAULT_FLAGS;
|
||||
|
||||
if (block.prevBlock !== this.tip.hash)
|
||||
throw new VerifyError(block, 'invalid', 'bad-prevblk', 0);
|
||||
|
||||
try {
|
||||
block._validHeaders = true;
|
||||
result = yield this.verifyContext(block, this.tip);
|
||||
} finally {
|
||||
block._validHeaders = valid;
|
||||
}
|
||||
flags &= ~common.flags.VERIFY_POW;
|
||||
|
||||
return result;
|
||||
return yield this.verifyContext(block, this.tip, flags);
|
||||
});
|
||||
|
||||
/**
|
||||
@ -303,30 +298,27 @@ Chain.prototype.isGenesis = function isGenesis(block) {
|
||||
* @private
|
||||
* @param {Block} block
|
||||
* @param {ChainEntry} prev
|
||||
* @param {Number} flags
|
||||
* @returns {Promise} - Returns {@link DeploymentState}.
|
||||
*/
|
||||
|
||||
Chain.prototype.verify = co(function* verify(block, prev) {
|
||||
Chain.prototype.verify = co(function* verify(block, prev, flags) {
|
||||
var ret = new VerifyResult();
|
||||
var now = this.network.now();
|
||||
var i, err, height, ts, tx, mtp;
|
||||
var i, height, ts, tx, mtp;
|
||||
var commit, state, bits;
|
||||
|
||||
assert(typeof flags === 'number');
|
||||
|
||||
// Non-contextual checks.
|
||||
if (!block.verify(ret)) {
|
||||
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.
|
||||
if (ret.reason !== 'high-hash')
|
||||
err.malleated = true;
|
||||
|
||||
throw err;
|
||||
if (flags & common.flags.VERIFY_BODY) {
|
||||
if (!block.verifyBody(ret)) {
|
||||
throw new VerifyError(block,
|
||||
'invalid',
|
||||
ret.reason,
|
||||
ret.score,
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
// Skip all blocks in spv mode.
|
||||
@ -357,12 +349,11 @@ Chain.prototype.verify = co(function* verify(block, prev) {
|
||||
// If this fails we may be able to accept
|
||||
// the block later.
|
||||
if (block.ts > now + 2 * 60 * 60) {
|
||||
err = new VerifyError(block,
|
||||
throw new VerifyError(block,
|
||||
'invalid',
|
||||
'time-too-new',
|
||||
0);
|
||||
err.malleated = true;
|
||||
throw err;
|
||||
0,
|
||||
true);
|
||||
}
|
||||
|
||||
// Get the new deployment state.
|
||||
@ -407,21 +398,19 @@ Chain.prototype.verify = co(function* verify(block, prev) {
|
||||
// "invalid" if either of these checks
|
||||
// fail.
|
||||
if (!block.getWitnessNonce()) {
|
||||
err = new VerifyError(block,
|
||||
throw new VerifyError(block,
|
||||
'invalid',
|
||||
'bad-witness-nonce-size',
|
||||
100);
|
||||
err.malleated = true;
|
||||
throw err;
|
||||
100,
|
||||
true);
|
||||
}
|
||||
|
||||
if (!util.equal(commit, block.createCommitmentHash())) {
|
||||
err = new VerifyError(block,
|
||||
throw new VerifyError(block,
|
||||
'invalid',
|
||||
'bad-witness-merkle-match',
|
||||
100);
|
||||
err.malleated = true;
|
||||
throw err;
|
||||
100,
|
||||
true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -430,12 +419,11 @@ Chain.prototype.verify = co(function* verify(block, prev) {
|
||||
// witness data cannot contain it.
|
||||
if (!commit) {
|
||||
if (block.hasWitness()) {
|
||||
err = new VerifyError(block,
|
||||
throw new VerifyError(block,
|
||||
'invalid',
|
||||
'unexpected-witness',
|
||||
100);
|
||||
err.malleated = true;
|
||||
throw err;
|
||||
100,
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -881,7 +869,7 @@ Chain.prototype.disconnect = co(function* disconnect(entry) {
|
||||
if (!block) {
|
||||
if (!this.options.spv)
|
||||
throw new Error('Block not found.');
|
||||
block = entry.toHeader();
|
||||
block = entry.toHeaders();
|
||||
}
|
||||
|
||||
prev = yield entry.getPrevious();
|
||||
@ -903,31 +891,33 @@ Chain.prototype.disconnect = co(function* disconnect(entry) {
|
||||
* in alternate chains when they come in).
|
||||
* @method
|
||||
* @param {ChainEntry} entry
|
||||
* @param {Number} flags
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
Chain.prototype.reconnect = co(function* reconnect(entry) {
|
||||
var block = yield this.db.getBlock(entry.hash);
|
||||
var flags = common.flags.VERIFY_NONE;
|
||||
var prev, result;
|
||||
|
||||
if (!block) {
|
||||
if (!this.options.spv)
|
||||
throw new Error('Block not found.');
|
||||
block = entry.toHeader();
|
||||
block = entry.toHeaders();
|
||||
}
|
||||
|
||||
prev = yield entry.getPrevious();
|
||||
assert(prev);
|
||||
|
||||
try {
|
||||
result = yield this.verifyContext(block, prev);
|
||||
} catch (e) {
|
||||
if (e.type === 'VerifyError') {
|
||||
if (!e.malleated)
|
||||
result = yield this.verifyContext(block, prev, flags);
|
||||
} catch (err) {
|
||||
if (err.type === 'VerifyError') {
|
||||
if (!err.malleated)
|
||||
this.setInvalid(entry.hash);
|
||||
this.emit('invalid', block, entry.height);
|
||||
}
|
||||
throw e;
|
||||
throw err;
|
||||
}
|
||||
|
||||
yield this.db.reconnect(entry, block, result.view);
|
||||
@ -951,10 +941,11 @@ Chain.prototype.reconnect = co(function* reconnect(entry) {
|
||||
* @param {ChainEntry} entry
|
||||
* @param {Block} block
|
||||
* @param {ChainEntry} prev
|
||||
* @param {Number} flags
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
Chain.prototype.setBestChain = co(function* setBestChain(entry, block, prev) {
|
||||
Chain.prototype.setBestChain = co(function* setBestChain(entry, block, prev, flags) {
|
||||
var result;
|
||||
|
||||
// A higher fork has arrived.
|
||||
@ -982,14 +973,14 @@ Chain.prototype.setBestChain = co(function* setBestChain(entry, block, prev) {
|
||||
// now that we're certain its previous
|
||||
// block is in the chain.
|
||||
try {
|
||||
result = yield this.verifyContext(block, prev);
|
||||
result = yield this.verifyContext(block, prev, flags);
|
||||
} catch (e) {
|
||||
if (e.type === 'VerifyError') {
|
||||
if (!e.malleated)
|
||||
if (err.type === 'VerifyError') {
|
||||
if (!err.malleated)
|
||||
this.setInvalid(entry.hash);
|
||||
this.emit('invalid', block, entry.height);
|
||||
}
|
||||
throw e;
|
||||
throw err;
|
||||
}
|
||||
|
||||
// Save block and connect inputs.
|
||||
@ -1012,21 +1003,22 @@ Chain.prototype.setBestChain = co(function* setBestChain(entry, block, prev) {
|
||||
* @param {ChainEntry} entry
|
||||
* @param {Block} block
|
||||
* @param {ChainEntry} prev
|
||||
* @param {Number} flags
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
Chain.prototype.saveAlternate = co(function* saveAlternate(entry, block, prev) {
|
||||
Chain.prototype.saveAlternate = co(function* saveAlternate(entry, block, prev, flags) {
|
||||
try {
|
||||
// Do as much verification
|
||||
// as we can before saving.
|
||||
yield this.verify(block, prev);
|
||||
} catch (e) {
|
||||
if (e.type === 'VerifyError') {
|
||||
if (!e.malleated)
|
||||
yield this.verify(block, prev, flags);
|
||||
} catch (err) {
|
||||
if (err.type === 'VerifyError') {
|
||||
if (!err.malleated)
|
||||
this.setInvalid(entry.hash);
|
||||
this.emit('invalid', block, entry.height);
|
||||
}
|
||||
throw e;
|
||||
throw err;
|
||||
}
|
||||
|
||||
// Warn of unknown versionbits.
|
||||
@ -1191,14 +1183,15 @@ Chain.prototype._resetTime = co(function* resetTime(ts) {
|
||||
* Add a block to the chain, perform all necessary verification.
|
||||
* @method
|
||||
* @param {Block} block
|
||||
* @param {Number} flags
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
Chain.prototype.add = co(function* add(block) {
|
||||
Chain.prototype.add = co(function* add(block, flags) {
|
||||
var hash = block.hash('hex');
|
||||
var unlock = yield this.locker.lock(hash);
|
||||
try {
|
||||
return yield this._add(block);
|
||||
return yield this._add(block, flags);
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
@ -1209,14 +1202,17 @@ Chain.prototype.add = co(function* add(block) {
|
||||
* @method
|
||||
* @private
|
||||
* @param {Block} block
|
||||
* @param {Number} flags
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
Chain.prototype._add = co(function* add(block) {
|
||||
var ret = new VerifyResult();
|
||||
Chain.prototype._add = co(function* add(block, flags) {
|
||||
var initial = true;
|
||||
var hash, entry, prev, result;
|
||||
|
||||
if (flags == null)
|
||||
flags = common.flags.DEFAULT_FLAGS;
|
||||
|
||||
assert(block);
|
||||
|
||||
while (block) {
|
||||
@ -1250,12 +1246,12 @@ Chain.prototype._add = co(function* add(block) {
|
||||
throw new VerifyError(block, 'duplicate', 'duplicate', 100);
|
||||
}
|
||||
|
||||
// Non-contextual verification.
|
||||
// If this is a memblock, it will
|
||||
// only be a POW validation.
|
||||
if (!block.verify(ret)) {
|
||||
this.emit('invalid', block, block.getCoinbaseHeight());
|
||||
throw new VerifyError(block, 'invalid', ret.reason, ret.score);
|
||||
// Check the POW before doing anything.
|
||||
if (flags & common.flags.VERIFY_POW) {
|
||||
if (!block.verifyPOW()) {
|
||||
this.emit('invalid', block, block.getCoinbaseHeight());
|
||||
throw new VerifyError(block, 'invalid', 'high-hash', 50);
|
||||
}
|
||||
}
|
||||
|
||||
// Do we already have this block?
|
||||
@ -1312,13 +1308,13 @@ Chain.prototype._add = co(function* add(block) {
|
||||
// connect the inputs.
|
||||
if (entry.chainwork.cmp(this.tip.chainwork) <= 0) {
|
||||
// Save block to an alternate chain.
|
||||
yield this.saveAlternate(entry, block, prev);
|
||||
yield this.saveAlternate(entry, block, prev, flags);
|
||||
|
||||
if (!initial)
|
||||
this.emit('competitor resolved', block, entry);
|
||||
} else {
|
||||
// Attempt to add block to the chain index.
|
||||
yield this.setBestChain(entry, block, prev);
|
||||
yield this.setBestChain(entry, block, prev, flags);
|
||||
|
||||
if (!initial)
|
||||
this.emit('resolved', block, entry);
|
||||
|
||||
@ -52,3 +52,25 @@ exports.thresholdStates = {
|
||||
ACTIVE: 3,
|
||||
FAILED: 4
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify flags for blocks.
|
||||
* @enum {Number}
|
||||
* @default
|
||||
*/
|
||||
|
||||
exports.flags = {
|
||||
VERIFY_NONE: 0,
|
||||
VERIFY_POW: 1 << 0,
|
||||
VERIFY_BODY: 1 << 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* Default block verify flags.
|
||||
* @const {Number}
|
||||
* @default
|
||||
*/
|
||||
|
||||
exports.flags.DEFAULT_FLAGS = 0
|
||||
| exports.flags.VERIFY_POW
|
||||
| exports.flags.VERIFY_BODY;
|
||||
|
||||
@ -4116,7 +4116,7 @@ RPC.prototype.importprunedfunds = co(function* importprunedfunds(args, help) {
|
||||
if (!block.verify())
|
||||
throw new RPCError('Invalid proof.');
|
||||
|
||||
if (!block.hasTX(tx))
|
||||
if (!block.hasTX(tx.hash('hex')))
|
||||
throw new RPCError('Invalid proof.');
|
||||
|
||||
height = yield this.chain.db.getHeight(hash);
|
||||
|
||||
@ -804,12 +804,11 @@ Mempool.prototype.verify = co(function* verify(entry, view) {
|
||||
}
|
||||
if (this.chain.state.hasWitness()) {
|
||||
if (!tx.hasStandardWitness(view, ret)) {
|
||||
ret = new VerifyError(tx,
|
||||
throw new VerifyError(tx,
|
||||
'nonstandard',
|
||||
ret.reason,
|
||||
ret.score);
|
||||
ret.malleated = ret.score > 0;
|
||||
throw ret;
|
||||
ret.score,
|
||||
ret.score > 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,6 +53,7 @@ function CompactBlock(options) {
|
||||
this.idMap = {};
|
||||
this.count = 0;
|
||||
this.sipKey = null;
|
||||
this.totalTX = 0;
|
||||
|
||||
if (options)
|
||||
this.fromOptions(options);
|
||||
@ -86,6 +87,9 @@ CompactBlock.prototype.fromOptions = function fromOptions(options) {
|
||||
if (options.count)
|
||||
this.count = options.count;
|
||||
|
||||
if (options.totalTX != null)
|
||||
this.totalTX = options.totalTX;
|
||||
|
||||
this.sipKey = options.sipKey;
|
||||
|
||||
this.initKey();
|
||||
@ -105,15 +109,14 @@ CompactBlock.fromOptions = function fromOptions(options) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify the block headers.
|
||||
* @alias CompactBlock#verify
|
||||
* Verify the block.
|
||||
* @param {Object?} ret - Return object, may be
|
||||
* set with properties `reason` and `score`.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
CompactBlock.prototype._verify = function _verify(ret) {
|
||||
return this.verifyHeaders(ret);
|
||||
CompactBlock.prototype.verifyBody = function verifyBody(ret) {
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -484,7 +487,6 @@ CompactBlock.prototype.toBlock = function toBlock() {
|
||||
block.totalTX = this.totalTX;
|
||||
block._hash = this._hash;
|
||||
block._hhash = this._hhash;
|
||||
block._validHeaders = this._validHeaders;
|
||||
|
||||
for (i = 0; i < this.available.length; i++) {
|
||||
tx = this.available[i];
|
||||
@ -516,8 +518,6 @@ CompactBlock.prototype.fromBlock = function fromBlock(block, witness, nonce) {
|
||||
this.totalTX = block.totalTX;
|
||||
this._hash = block._hash;
|
||||
this._hhash = block._hhash;
|
||||
this._validHeaders = true;
|
||||
this._valid = true;
|
||||
|
||||
if (!nonce)
|
||||
nonce = util.nonce();
|
||||
|
||||
@ -14,6 +14,7 @@ var util = require('../utils/util');
|
||||
var IP = require('../utils/ip');
|
||||
var co = require('../utils/co');
|
||||
var common = require('./common');
|
||||
var chainCommon = require('../blockchain/common');
|
||||
var NetAddress = require('../primitives/netaddress');
|
||||
var Address = require('../primitives/address');
|
||||
var BIP150 = require('./bip150');
|
||||
@ -1921,6 +1922,8 @@ Pool.prototype.handleSendHeaders = co(function* handleSendHeaders(peer, packet)
|
||||
*/
|
||||
|
||||
Pool.prototype.handleBlock = co(function* handleBlock(peer, packet) {
|
||||
var flags = chainCommon.flags.DEFAULT_FLAGS;
|
||||
|
||||
if (this.options.spv) {
|
||||
this.logger.warning(
|
||||
'Peer sent unsolicited block (%s).',
|
||||
@ -1928,7 +1931,7 @@ Pool.prototype.handleBlock = co(function* handleBlock(peer, packet) {
|
||||
return;
|
||||
}
|
||||
|
||||
return yield this.addBlock(peer, packet.block);
|
||||
return yield this.addBlock(peer, packet.block, flags);
|
||||
});
|
||||
|
||||
/**
|
||||
@ -1940,11 +1943,11 @@ Pool.prototype.handleBlock = co(function* handleBlock(peer, packet) {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
Pool.prototype.addBlock = co(function* addBlock(peer, block) {
|
||||
Pool.prototype.addBlock = co(function* addBlock(peer, block, flags) {
|
||||
var hash = block.hash('hex');
|
||||
var unlock = yield this.locker.lock(hash);
|
||||
try {
|
||||
return yield this._addBlock(peer, block);
|
||||
return yield this._addBlock(peer, block, flags);
|
||||
} finally {
|
||||
unlock();
|
||||
}
|
||||
@ -1959,7 +1962,7 @@ Pool.prototype.addBlock = co(function* addBlock(peer, block) {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
Pool.prototype._addBlock = co(function* addBlock(peer, block) {
|
||||
Pool.prototype._addBlock = co(function* addBlock(peer, block, flags) {
|
||||
var hash = block.hash('hex');
|
||||
|
||||
if (!this.syncing)
|
||||
@ -1976,7 +1979,7 @@ Pool.prototype._addBlock = co(function* addBlock(peer, block) {
|
||||
peer.blockTime = util.ms();
|
||||
|
||||
try {
|
||||
yield this.chain.add(block);
|
||||
yield this.chain.add(block, flags);
|
||||
} catch (err) {
|
||||
if (err.type === 'VerifyError') {
|
||||
if (err.reason === 'bad-prevblk') {
|
||||
@ -2145,6 +2148,7 @@ Pool.prototype.handleTX = co(function* handleTX(peer, packet) {
|
||||
Pool.prototype._handleTX = co(function* handleTX(peer, packet) {
|
||||
var tx = packet.tx;
|
||||
var hash = tx.hash('hex');
|
||||
var flags = chainCommon.flags.VERIFY_NONE;
|
||||
var missing;
|
||||
|
||||
if (peer.merkleBlock) {
|
||||
@ -2152,7 +2156,7 @@ Pool.prototype._handleTX = co(function* handleTX(peer, packet) {
|
||||
if (peer.merkleBlock.hasTX(tx)) {
|
||||
peer.merkleBlock.addTX(tx);
|
||||
if (--peer.merkleMatches === 0) {
|
||||
yield this._addBlock(peer, peer.merkleBlock);
|
||||
yield this._addBlock(peer, peer.merkleBlock, flags);
|
||||
peer.merkleTime = -1;
|
||||
peer.merkleBlock = null;
|
||||
peer.merkleMatches = 0;
|
||||
@ -2332,6 +2336,7 @@ Pool.prototype.handleMerkleBlock = co(function* handleMerkleBlock(peer, packet)
|
||||
Pool.prototype._handleMerkleBlock = co(function* handleMerkleBlock(peer, packet) {
|
||||
var block = packet.block;
|
||||
var hash = block.hash('hex');
|
||||
var flags = chainCommon.flags.VERIFY_NONE;
|
||||
|
||||
if (!this.syncing)
|
||||
return;
|
||||
@ -2369,14 +2374,14 @@ Pool.prototype._handleMerkleBlock = co(function* handleMerkleBlock(peer, packet)
|
||||
return;
|
||||
}
|
||||
|
||||
if (block.matches.length === 0) {
|
||||
yield this._addBlock(peer, block);
|
||||
if (block.tree.matches.length === 0) {
|
||||
yield this._addBlock(peer, block, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
peer.merkleTime = util.ms();
|
||||
peer.merkleBlock = block;
|
||||
peer.merkleMatches = block.matches.length;
|
||||
peer.merkleMatches = block.tree.matches.length;
|
||||
});
|
||||
|
||||
/**
|
||||
@ -2415,6 +2420,7 @@ Pool.prototype.handleCmpctBlock = co(function* handleCmpctBlock(peer, packet) {
|
||||
var block = packet.block;
|
||||
var hash = block.hash('hex');
|
||||
var witness = peer.hasWitness();
|
||||
var flags = chainCommon.flags.VERIFY_BODY;
|
||||
var result;
|
||||
|
||||
if (!this.syncing)
|
||||
@ -2473,7 +2479,7 @@ Pool.prototype.handleCmpctBlock = co(function* handleCmpctBlock(peer, packet) {
|
||||
this.logger.debug(
|
||||
'Received full compact block %s (%s).',
|
||||
block.rhash(), peer.hostname());
|
||||
yield this.addBlock(peer, block.toBlock());
|
||||
yield this.addBlock(peer, block.toBlock(), flags);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2555,6 +2561,7 @@ Pool.prototype.handleGetBlockTxn = co(function* handleGetBlockTxn(peer, packet)
|
||||
Pool.prototype.handleBlockTxn = co(function* handleBlockTxn(peer, packet) {
|
||||
var res = packet.response;
|
||||
var block = peer.compactBlocks.get(res.hash);
|
||||
var flags = chainCommon.flags.VERIFY_BODY;
|
||||
|
||||
if (!block) {
|
||||
this.logger.debug(
|
||||
@ -2580,7 +2587,7 @@ Pool.prototype.handleBlockTxn = co(function* handleBlockTxn(peer, packet) {
|
||||
'Filled compact block %s (%s).',
|
||||
block.rhash(), peer.hostname());
|
||||
|
||||
yield this.addBlock(peer, block.toBlock());
|
||||
yield this.addBlock(peer, block.toBlock(), flags);
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@ -10,7 +10,6 @@
|
||||
var assert = require('assert');
|
||||
var util = require('../utils/util');
|
||||
var crypto = require('../crypto/crypto');
|
||||
var VerifyResult = require('../protocol/errors').VerifyResult;
|
||||
var StaticWriter = require('../utils/staticwriter');
|
||||
var InvItem = require('./invitem');
|
||||
var encoding = require('../utils/encoding');
|
||||
@ -30,7 +29,6 @@ var consensus = require('../protocol/consensus');
|
||||
* @property {Number} ts - Timestamp.
|
||||
* @property {Number} bits
|
||||
* @property {Number} nonce
|
||||
* @property {Number} totalTX - Transaction count.
|
||||
* @property {TX[]} txs - Transaction vector.
|
||||
* @property {ReversedHash} rhash - Reversed block hash (uint256le).
|
||||
*/
|
||||
@ -45,14 +43,10 @@ function AbstractBlock() {
|
||||
this.ts = 0;
|
||||
this.bits = 0;
|
||||
this.nonce = 0;
|
||||
this.totalTX = 0;
|
||||
|
||||
this.txs = null;
|
||||
this.mutable = false;
|
||||
|
||||
this._valid = null;
|
||||
this._validHeaders = null;
|
||||
|
||||
this._hash = null;
|
||||
this._hhash = null;
|
||||
this._size = -1;
|
||||
@ -90,11 +84,6 @@ AbstractBlock.prototype.parseOptions = function parseOptions(options) {
|
||||
this.bits = options.bits;
|
||||
this.nonce = options.nonce;
|
||||
|
||||
if (options.totalTX != null) {
|
||||
assert(util.isNumber(options.totalTX));
|
||||
this.totalTX = options.totalTX;
|
||||
}
|
||||
|
||||
if (options.mutable != null)
|
||||
this.mutable = !!options.mutable;
|
||||
|
||||
@ -115,7 +104,6 @@ AbstractBlock.prototype.parseJSON = function parseJSON(json) {
|
||||
assert(util.isNumber(json.ts));
|
||||
assert(util.isNumber(json.bits));
|
||||
assert(util.isNumber(json.nonce));
|
||||
assert(util.isNumber(json.totalTX));
|
||||
|
||||
this.version = json.version;
|
||||
this.prevBlock = util.revHex(json.prevBlock);
|
||||
@ -123,7 +111,6 @@ AbstractBlock.prototype.parseJSON = function parseJSON(json) {
|
||||
this.ts = json.ts;
|
||||
this.bits = json.bits;
|
||||
this.nonce = json.nonce;
|
||||
this.totalTX = json.totalTX;
|
||||
|
||||
return this;
|
||||
};
|
||||
@ -136,8 +123,6 @@ AbstractBlock.prototype.parseJSON = function parseJSON(json) {
|
||||
AbstractBlock.prototype._refresh = function refresh(all) {
|
||||
var i, tx;
|
||||
|
||||
this._valid = null;
|
||||
this._validHeaders = null;
|
||||
this._hash = null;
|
||||
this._hhash = null;
|
||||
this._size = -1;
|
||||
@ -234,74 +219,15 @@ AbstractBlock.prototype.parseAbbr = function parseAbbr(br) {
|
||||
|
||||
/**
|
||||
* Verify the block.
|
||||
* @param {Object?} ret - Return object, may be
|
||||
* set with properties `reason` and `score`.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
AbstractBlock.prototype.verify = function verify(ret) {
|
||||
var valid = this._valid;
|
||||
|
||||
if (valid == null) {
|
||||
valid = this._verify(ret);
|
||||
if (!this.mutable)
|
||||
this._valid = valid;
|
||||
}
|
||||
|
||||
return valid;
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify the block.
|
||||
* @private
|
||||
* @param {Object?} ret - Return object, may be
|
||||
* set with properties `reason` and `score`.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
AbstractBlock.prototype._verify = function verify(ret) {
|
||||
throw new Error('Abstract method.');
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify the block headers (called by `verify()` in
|
||||
* all objects which inherit from AbstractBlock).
|
||||
* @param {Object?} ret - Return object, may be
|
||||
* set with properties `reason` and `score`.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
AbstractBlock.prototype.verifyHeaders = function verifyHeaders(ret) {
|
||||
var valid = this._validHeaders;
|
||||
|
||||
if (valid == null) {
|
||||
valid = this._verifyHeaders(ret);
|
||||
if (!this.mutable)
|
||||
this._validHeaders = valid;
|
||||
}
|
||||
|
||||
return valid;
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify the block headers (called by `verify()` in
|
||||
* all objects which inherit from AbstractBlock).
|
||||
* @private
|
||||
* @param {Object?} ret - Return object, may be
|
||||
* set with properties `reason` and `score`.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
AbstractBlock.prototype._verifyHeaders = function verifyHeaders(ret) {
|
||||
if (!ret)
|
||||
ret = new VerifyResult();
|
||||
|
||||
// Check proof of work.
|
||||
if (!this.verifyPOW()) {
|
||||
ret.reason = 'high-hash';
|
||||
ret.score = 50;
|
||||
AbstractBlock.prototype.verify = function verify() {
|
||||
if (!this.verifyPOW())
|
||||
return false;
|
||||
|
||||
if (!this.verifyBody())
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
@ -315,6 +241,17 @@ AbstractBlock.prototype.verifyPOW = function verifyPOW() {
|
||||
return consensus.verifyPOW(this.hash(), this.bits);
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify the block.
|
||||
* @param {Object?} ret - Return object, may be
|
||||
* set with properties `reason` and `score`.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
AbstractBlock.prototype.verifyBody = function verifyBody(ret) {
|
||||
throw new Error('Abstract method.');
|
||||
};
|
||||
|
||||
/**
|
||||
* Get little-endian block hash.
|
||||
* @returns {Hash}
|
||||
|
||||
@ -270,9 +270,6 @@ Block.prototype.hasTX = function hasTX(hash) {
|
||||
Block.prototype.indexOf = function indexOf(hash) {
|
||||
var i, tx;
|
||||
|
||||
if (hash instanceof TX)
|
||||
hash = hash.hash('hex');
|
||||
|
||||
for (i = 0; i < this.txs.length; i++) {
|
||||
tx = this.txs[i];
|
||||
if (tx.hash('hex') === hash)
|
||||
@ -430,7 +427,7 @@ Block.prototype.getCommitmentHash = function getCommitmentHash(enc) {
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
Block.prototype._verify = function _verify(ret) {
|
||||
Block.prototype.verifyBody = function verifyBody(ret) {
|
||||
var sigops = 0;
|
||||
var scale = consensus.WITNESS_SCALE_FACTOR;
|
||||
var i, tx, merkle;
|
||||
@ -438,9 +435,6 @@ Block.prototype._verify = function _verify(ret) {
|
||||
if (!ret)
|
||||
ret = new VerifyResult();
|
||||
|
||||
if (!this.verifyHeaders(ret))
|
||||
return false;
|
||||
|
||||
// Check merkle root.
|
||||
merkle = this.createMerkleRoot('hex');
|
||||
|
||||
@ -634,7 +628,6 @@ Block.prototype.getJSON = function getJSON(network, view, height) {
|
||||
ts: this.ts,
|
||||
bits: this.bits,
|
||||
nonce: this.nonce,
|
||||
totalTX: this.totalTX,
|
||||
txs: this.txs.map(function(tx, i) {
|
||||
return tx.getJSON(network, view, null, i);
|
||||
}, this)
|
||||
@ -679,15 +672,15 @@ Block.fromJSON = function fromJSON(json) {
|
||||
|
||||
Block.prototype.fromReader = function fromReader(br) {
|
||||
var witness = 0;
|
||||
var i, tx;
|
||||
var i, count, tx;
|
||||
|
||||
br.start();
|
||||
|
||||
this.parseAbbr(br);
|
||||
|
||||
this.totalTX = br.readVarint();
|
||||
count = br.readVarint();
|
||||
|
||||
for (i = 0; i < this.totalTX; i++) {
|
||||
for (i = 0; i < count; i++) {
|
||||
tx = TX.fromReader(br);
|
||||
witness += tx._witness;
|
||||
this.addTX(tx);
|
||||
|
||||
@ -9,7 +9,6 @@
|
||||
|
||||
var util = require('../utils/util');
|
||||
var AbstractBlock = require('./abstractblock');
|
||||
var encoding = require('../utils/encoding');
|
||||
var StaticWriter = require('../utils/staticwriter');
|
||||
var BufferReader = require('../utils/reader');
|
||||
|
||||
@ -35,14 +34,13 @@ util.inherits(Headers, AbstractBlock);
|
||||
|
||||
/**
|
||||
* Do non-contextual verification on the headers.
|
||||
* @alias Headers#verify
|
||||
* @param {Object?} ret - Return object, may be
|
||||
* set with properties `reason` and `score`.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
Headers.prototype._verify = function _verify(ret) {
|
||||
return this.verifyHeaders(ret);
|
||||
Headers.prototype.verifyBody = function verifyBody(ret) {
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -51,7 +49,7 @@ Headers.prototype._verify = function _verify(ret) {
|
||||
*/
|
||||
|
||||
Headers.prototype.getSize = function getSize() {
|
||||
return 80 + encoding.sizeVarint(this.totalTX);
|
||||
return 81;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -61,7 +59,7 @@ Headers.prototype.getSize = function getSize() {
|
||||
|
||||
Headers.prototype.toWriter = function toWriter(bw) {
|
||||
this.writeAbbr(bw);
|
||||
bw.writeVarint(this.totalTX);
|
||||
bw.writeVarint(0);
|
||||
return bw;
|
||||
};
|
||||
|
||||
@ -83,7 +81,7 @@ Headers.prototype.toRaw = function toRaw() {
|
||||
|
||||
Headers.prototype.fromReader = function fromReader(br) {
|
||||
this.parseAbbr(br);
|
||||
this.totalTX = br.readVarint();
|
||||
br.readVarint();
|
||||
return this;
|
||||
};
|
||||
|
||||
@ -172,7 +170,6 @@ Headers.fromAbbr = function fromAbbr(data, enc) {
|
||||
Headers.fromEntry = function fromEntry(entry) {
|
||||
var headers = new Headers(entry);
|
||||
headers._hash = new Buffer(entry.hash, 'hex');
|
||||
headers._valid = true;
|
||||
return headers;
|
||||
};
|
||||
|
||||
@ -195,7 +192,6 @@ Headers.fromBlock = function fromBlock(block) {
|
||||
var headers = new Headers(block);
|
||||
headers._hash = block._hash;
|
||||
headers._hhash = block._hhash;
|
||||
headers._valid = true;
|
||||
return headers;
|
||||
};
|
||||
|
||||
@ -229,8 +225,7 @@ Headers.prototype.getJSON = function getJSON(network, view, height) {
|
||||
merkleRoot: util.revHex(this.merkleRoot),
|
||||
ts: this.ts,
|
||||
bits: this.bits,
|
||||
nonce: this.nonce,
|
||||
totalTX: this.totalTX
|
||||
nonce: this.nonce
|
||||
};
|
||||
};
|
||||
|
||||
@ -283,8 +278,7 @@ Headers.prototype.format = function format(view, height) {
|
||||
merkleRoot: util.revHex(this.merkleRoot),
|
||||
ts: this.ts,
|
||||
bits: this.bits,
|
||||
nonce: this.nonce,
|
||||
totalTX: this.totalTX
|
||||
nonce: this.nonce
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -73,15 +73,14 @@ MemBlock.prototype.getSize = function getSize() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify the block headers.
|
||||
* @alias MemBlock#verify
|
||||
* Verify the block.
|
||||
* @param {Object?} ret - Return object, may be
|
||||
* set with properties `reason` and `score`.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
MemBlock.prototype._verify = function _verify(ret) {
|
||||
return this.verifyHeaders(ret);
|
||||
MemBlock.prototype.verifyBody = function verifyBody(ret) {
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -103,25 +102,25 @@ MemBlock.prototype.getCoinbaseHeight = function getCoinbaseHeight() {
|
||||
MemBlock.prototype.fromRaw = function fromRaw(data) {
|
||||
var br = new BufferReader(data, true);
|
||||
var height = -1;
|
||||
var inCount, input;
|
||||
var count, script;
|
||||
|
||||
this.parseAbbr(br);
|
||||
|
||||
this.totalTX = br.readVarint();
|
||||
count = br.readVarint();
|
||||
|
||||
if (this.version > 1 && this.totalTX > 0) {
|
||||
if (this.version > 1 && count > 0) {
|
||||
br.seek(4);
|
||||
inCount = br.readVarint();
|
||||
count = br.readVarint();
|
||||
|
||||
if (inCount === 0) {
|
||||
if (count === 0) {
|
||||
if (br.readU8() !== 0)
|
||||
inCount = br.readVarint();
|
||||
count = br.readVarint();
|
||||
}
|
||||
|
||||
if (inCount > 0) {
|
||||
if (count > 0) {
|
||||
br.seek(36);
|
||||
input = br.readVarBytes();
|
||||
height = Script.getCoinbaseHeight(input);
|
||||
script = br.readVarBytes();
|
||||
height = Script.getCoinbaseHeight(script);
|
||||
}
|
||||
}
|
||||
|
||||
@ -171,7 +170,6 @@ MemBlock.prototype.toBlock = function toBlock() {
|
||||
block._hash = this._hash;
|
||||
block._hhash = this._hhash;
|
||||
block._cbHeight = this._cbHeight;
|
||||
block._validHeaders = this._validHeaders;
|
||||
this._raw = null;
|
||||
return block;
|
||||
};
|
||||
|
||||
@ -17,7 +17,6 @@ var StaticWriter = require('../utils/staticwriter');
|
||||
var encoding = require('../utils/encoding');
|
||||
var consensus = require('../protocol/consensus');
|
||||
var Headers = require('./headers');
|
||||
var TX = require('./tx');
|
||||
var DUMMY = new Buffer([0]);
|
||||
|
||||
/**
|
||||
@ -37,12 +36,8 @@ function MerkleBlock(options) {
|
||||
this.hashes = [];
|
||||
this.flags = DUMMY;
|
||||
|
||||
// List of matched TXs
|
||||
this.map = {};
|
||||
this.matches = [];
|
||||
this._validPartial = null;
|
||||
|
||||
// TXs that will be pushed on
|
||||
this.totalTX = 0;
|
||||
this.tree = null;
|
||||
this.txs = [];
|
||||
|
||||
if (options)
|
||||
@ -65,6 +60,7 @@ MerkleBlock.prototype.fromOptions = function fromOptions(options) {
|
||||
assert(options, 'MerkleBlock data is required.');
|
||||
assert(Array.isArray(options.hashes));
|
||||
assert(Buffer.isBuffer(options.flags));
|
||||
assert(util.isUInt32(options.totalTX));
|
||||
|
||||
if (options.hashes) {
|
||||
for (i = 0; i < options.hashes.length; i++) {
|
||||
@ -78,6 +74,9 @@ MerkleBlock.prototype.fromOptions = function fromOptions(options) {
|
||||
if (options.flags)
|
||||
this.flags = options.flags;
|
||||
|
||||
if (options.totalTX != null)
|
||||
this.totalTX = options.totalTX;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
@ -97,9 +96,7 @@ MerkleBlock.fromOptions = function fromOptions(data) {
|
||||
*/
|
||||
|
||||
MerkleBlock.prototype.refresh = function refresh(all) {
|
||||
this.map = {};
|
||||
this.matches.length = 0;
|
||||
this._validPartial = null;
|
||||
this.tree = null;
|
||||
this._refresh(all);
|
||||
};
|
||||
|
||||
@ -110,8 +107,9 @@ MerkleBlock.prototype.refresh = function refresh(all) {
|
||||
*/
|
||||
|
||||
MerkleBlock.prototype.addTX = function addTX(tx) {
|
||||
var tree = this.getTree();
|
||||
var hash = tx.hash('hex');
|
||||
var index = this.map[hash];
|
||||
var index = tree.map[hash];
|
||||
|
||||
this.txs.push(tx);
|
||||
|
||||
@ -120,7 +118,7 @@ MerkleBlock.prototype.addTX = function addTX(tx) {
|
||||
|
||||
/**
|
||||
* Test the block's _matched_ transaction vector against a hash.
|
||||
* @param {Hash|TX} hash
|
||||
* @param {Hash} hash
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
@ -130,19 +128,13 @@ MerkleBlock.prototype.hasTX = function hasTX(hash) {
|
||||
|
||||
/**
|
||||
* Test the block's _matched_ transaction vector against a hash.
|
||||
* @param {Hash|TX} hash
|
||||
* @param {Hash} hash
|
||||
* @returns {Number} Index.
|
||||
*/
|
||||
|
||||
MerkleBlock.prototype.indexOf = function indexOf(hash) {
|
||||
var index;
|
||||
|
||||
if (hash instanceof TX)
|
||||
hash = hash.hash('hex');
|
||||
|
||||
this.verifyPartial();
|
||||
|
||||
index = this.map[hash];
|
||||
var tree = this.getTree();
|
||||
var index = tree.map[hash];
|
||||
|
||||
if (index == null)
|
||||
return -1;
|
||||
@ -150,6 +142,25 @@ MerkleBlock.prototype.indexOf = function indexOf(hash) {
|
||||
return index;
|
||||
};
|
||||
|
||||
/**
|
||||
* Do non-contextual verification on the block.
|
||||
* Verify the headers and the partial merkle tree.
|
||||
* @alias MerkleBlock#verify
|
||||
* @param {Object?} ret - Return object, may be
|
||||
* set with properties `reason` and `score`.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
MerkleBlock.prototype.verify = function verify(ret) {
|
||||
if (!this.verifyPOW())
|
||||
return false;
|
||||
|
||||
if (!this.verifyBody())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify the partial merkletree. Push leaves onto
|
||||
* {@link MerkleBlock#tx} and into {@link MerkleBlock#map}.
|
||||
@ -157,31 +168,38 @@ MerkleBlock.prototype.indexOf = function indexOf(hash) {
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
MerkleBlock.prototype.verifyPartial = function verifyPartial() {
|
||||
var tree;
|
||||
MerkleBlock.prototype.verifyBody = function verifyBody(ret) {
|
||||
var tree = this.getTree();
|
||||
|
||||
if (this._validPartial != null)
|
||||
return this._validPartial;
|
||||
|
||||
try {
|
||||
tree = this.extractTree();
|
||||
} catch (e) {
|
||||
this._validPartial = false;
|
||||
return false;
|
||||
}
|
||||
if (!ret)
|
||||
ret = new VerifyResult();
|
||||
|
||||
if (tree.root !== this.merkleRoot) {
|
||||
this._validPartial = false;
|
||||
ret.reason = 'bad-txnmrklroot';
|
||||
ret.score = 100;
|
||||
return false;
|
||||
}
|
||||
|
||||
this.matches = tree.matches;
|
||||
this.map = tree.map;
|
||||
this._validPartial = true;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Extract the matches from partial merkle
|
||||
* tree and calculate merkle root.
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
MerkleBlock.prototype.getTree = function getTree() {
|
||||
if (!this.tree) {
|
||||
try {
|
||||
this.tree = this.extractTree();
|
||||
} catch (e) {
|
||||
this.tree = new PartialTree();
|
||||
}
|
||||
}
|
||||
return this.tree;
|
||||
};
|
||||
|
||||
/**
|
||||
* Extract the matches from partial merkle
|
||||
* tree and calculate merkle root.
|
||||
@ -279,31 +297,6 @@ MerkleBlock.prototype.extractTree = function extractTree() {
|
||||
return new PartialTree(root, matches, indexes, map);
|
||||
};
|
||||
|
||||
/**
|
||||
* Do non-contextual verification on the block.
|
||||
* Verify the headers and the partial merkle tree.
|
||||
* @alias MerkleBlock#verify
|
||||
* @param {Object?} ret - Return object, may be
|
||||
* set with properties `reason` and `score`.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
MerkleBlock.prototype._verify = function _verify(ret) {
|
||||
if (!ret)
|
||||
ret = new VerifyResult();
|
||||
|
||||
if (!this.verifyHeaders(ret))
|
||||
return false;
|
||||
|
||||
if (!this.verifyPartial()) {
|
||||
ret.reason = 'bad-txnmrklroot';
|
||||
ret.score = 100;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Extract the coinbase height (always -1).
|
||||
* @returns {Number}
|
||||
@ -347,7 +340,7 @@ MerkleBlock.prototype.format = function format(view, height) {
|
||||
return hash.toString('hex');
|
||||
}),
|
||||
flags: this.flags,
|
||||
map: this.map,
|
||||
map: this.getTree().map,
|
||||
txs: this.txs.length
|
||||
};
|
||||
};
|
||||
@ -508,6 +501,7 @@ MerkleBlock.prototype.fromJSON = function fromJSON(json) {
|
||||
assert(json, 'MerkleBlock data is required.');
|
||||
assert(Array.isArray(json.hashes));
|
||||
assert(typeof json.flags === 'string');
|
||||
assert(util.isUInt32(json.totalTX));
|
||||
|
||||
this.parseJSON(json);
|
||||
|
||||
@ -518,6 +512,8 @@ MerkleBlock.prototype.fromJSON = function fromJSON(json) {
|
||||
|
||||
this.flags = new Buffer(json.flags, 'hex');
|
||||
|
||||
this.totalTX = json.totalTX;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
@ -687,8 +683,8 @@ MerkleBlock.fromMatches = function fromMatches(block, matches) {
|
||||
|
||||
MerkleBlock.isMerkleBlock = function isMerkleBlock(obj) {
|
||||
return obj
|
||||
&& obj.flags !== undefined
|
||||
&& typeof obj.verifyPartial === 'function';
|
||||
&& Buffer.isBuffer(obj.flags)
|
||||
&& typeof obj.verifyBody === 'function';
|
||||
};
|
||||
|
||||
/**
|
||||
@ -705,10 +701,10 @@ MerkleBlock.prototype.toHeaders = function toHeaders() {
|
||||
*/
|
||||
|
||||
function PartialTree(root, matches, indexes, map) {
|
||||
this.root = root.toString('hex');
|
||||
this.matches = matches;
|
||||
this.indexes = indexes;
|
||||
this.map = map;
|
||||
this.root = root ? root.toString('hex') : encoding.NULL_HASH;
|
||||
this.matches = matches || [];
|
||||
this.indexes = indexes || [];
|
||||
this.map = map || {};
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@ -26,14 +26,16 @@ var util = require('../utils/util');
|
||||
* @param {String} reason - Reject packet reason.
|
||||
* @param {Number} score - Ban score increase
|
||||
* (can be -1 for no reject packet).
|
||||
* @param {Boolean} malleated
|
||||
* @property {String} code
|
||||
* @property {Buffer} hash
|
||||
* @property {Number} height (will be the coinbase height if not present).
|
||||
* @property {Number} score
|
||||
* @property {String} message
|
||||
* @property {Boolean} malleated
|
||||
*/
|
||||
|
||||
function VerifyError(msg, code, reason, score) {
|
||||
function VerifyError(msg, code, reason, score, malleated) {
|
||||
Error.call(this);
|
||||
|
||||
if (Error.captureStackTrace)
|
||||
@ -48,13 +50,11 @@ function VerifyError(msg, code, reason, score) {
|
||||
this.code = code;
|
||||
this.reason = reason;
|
||||
this.score = score;
|
||||
|
||||
this.hash = msg.hash();
|
||||
this.malleated = false;
|
||||
this.malleated = malleated || false;
|
||||
|
||||
this.message = 'Verification failure: ' + reason
|
||||
+ ' (code=' + code + ', score=' + score
|
||||
+ ', hash=' + util.revHex(this.hash.toString('hex'))
|
||||
+ ', hash=' + msg.rhash()
|
||||
+ ')';
|
||||
}
|
||||
|
||||
|
||||
@ -100,17 +100,24 @@ describe('Block', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
it('should parse partial merkle tree', function() {
|
||||
var tree;
|
||||
|
||||
assert(mblock.verifyPOW());
|
||||
assert(mblock.verifyBody());
|
||||
assert(mblock.verify());
|
||||
assert.equal(mblock.matches.length, 2);
|
||||
|
||||
tree = mblock.getTree();
|
||||
|
||||
assert.equal(tree.matches.length, 2);
|
||||
assert.equal(mblock.hash('hex'),
|
||||
'8cc72c02a958de5a8b35a23bb7e3bced8bf840cc0a4e1c820000000000000000');
|
||||
assert.equal(mblock.rhash(),
|
||||
'0000000000000000821c4e0acc40f88bedbce3b73ba2358b5ade58a9022cc78c');
|
||||
assert.equal(
|
||||
mblock.matches[0].toString('hex'),
|
||||
tree.matches[0].toString('hex'),
|
||||
'7393f84cd04ca8931975c66282ebf1847c78d8de6c2578d4f9bae23bc6f30857');
|
||||
assert.equal(
|
||||
mblock.matches[1].toString('hex'),
|
||||
tree.matches[1].toString('hex'),
|
||||
'ec8c51de3170301430ec56f6703533d9ea5b05c6fa7068954bcb90eed8c2ee5c');
|
||||
});
|
||||
|
||||
@ -181,7 +188,7 @@ describe('Block', function() {
|
||||
|
||||
mblock2 = MerkleBlock.fromBlock(block, filter);
|
||||
|
||||
assert(mblock2.verifyPartial());
|
||||
assert(mblock2.verifyBody());
|
||||
assert.deepEqual(mblock2.toRaw(), mblock.toRaw());
|
||||
});
|
||||
|
||||
@ -230,10 +237,11 @@ describe('Block', function() {
|
||||
it('should fail with a bad merkle root', function() {
|
||||
var block2 = new Block(block);
|
||||
var ret = {};
|
||||
block2.hash();
|
||||
block2.merkleRoot = encoding.NULL_HASH;
|
||||
block2._validHeaders = null;
|
||||
assert(!block2.verify(ret));
|
||||
block2.refresh();
|
||||
assert(!block2.verifyPOW());
|
||||
assert(!block2.verifyBody(ret));
|
||||
assert(!block2.verify());
|
||||
assert.equal(ret.reason, 'bad-txnmrklroot');
|
||||
block2.merkleRoot = block.merkleRoot;
|
||||
block2.refresh();
|
||||
@ -243,9 +251,11 @@ describe('Block', function() {
|
||||
it('should fail on merkle block with a bad merkle root', function() {
|
||||
var mblock2 = new MerkleBlock(mblock);
|
||||
var ret = {};
|
||||
mblock2.hash();
|
||||
mblock2.merkleRoot = encoding.NULL_HASH;
|
||||
assert(!mblock2.verify(ret));
|
||||
mblock2.refresh();
|
||||
assert(!mblock2.verifyPOW());
|
||||
assert(!mblock2.verifyBody(ret));
|
||||
assert(!mblock2.verify());
|
||||
assert.equal(ret.reason, 'bad-txnmrklroot');
|
||||
mblock2.merkleRoot = mblock.merkleRoot;
|
||||
mblock2.refresh();
|
||||
@ -254,11 +264,11 @@ describe('Block', function() {
|
||||
|
||||
it('should fail with a low target', function() {
|
||||
var block2 = new Block(block);
|
||||
var ret = {};
|
||||
block2.hash();
|
||||
block2.bits = 403014710;
|
||||
assert(!block2.verify(ret));
|
||||
assert.equal(ret.reason, 'high-hash');
|
||||
block2.refresh();
|
||||
assert(!block2.verifyPOW());
|
||||
assert(block2.verifyBody());
|
||||
assert(!block2.verify());
|
||||
block2.bits = block.bits;
|
||||
block2.refresh();
|
||||
assert(block2.verify());
|
||||
@ -268,12 +278,15 @@ describe('Block', function() {
|
||||
var block2 = new Block(block);
|
||||
var ret = {};
|
||||
block2.txs.push(block2.txs[block2.txs.length - 1]);
|
||||
assert(!block2.verify(ret));
|
||||
block2.refresh();
|
||||
assert(!block2.verifyBody(ret));
|
||||
assert.equal(ret.reason, 'bad-txns-duplicate');
|
||||
});
|
||||
|
||||
it('should verify with headers', function() {
|
||||
var headers = new Headers(block);
|
||||
assert(headers.verifyPOW());
|
||||
assert(headers.verifyBody());
|
||||
assert(headers.verify());
|
||||
});
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user