more refactoring.

This commit is contained in:
Christopher Jeffrey 2016-02-16 16:13:34 -08:00
parent ab610d6fa5
commit 997f0acbb0
12 changed files with 213 additions and 397 deletions

View File

@ -299,194 +299,6 @@ Block.prototype._verify = function _verify() {
return true; return true;
}; };
Block.prototype.verifyContext = function verifyContext() {
var flags = {};
var sigops = 0;
var prev, height, ts, i, j, tx, cb, input;
if (this.isGenesis())
return true;
if (!this.chain)
return true;
prev = this.chain.getBlock(this.prevBlock);
// Ensure it's not an orphan
if (!prev) {
utils.debug('Block has no previous entry: %s', this.rhash);
return false;
}
height = prev.height + 1;
// Ensure the timestamp is correct
if (this.ts <= prev.getMedianTime()) {
utils.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.getTarget(prev, this)) {
utils.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)) {
utils.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)) {
utils.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)) {
utils.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)) {
// utils.debug('Block is outdated (v8): %s', this.rhash);
// return false;
// }
// Can't verify any further when merkleblock or headers.
if (this.subtype !== 'block')
return true;
// Make sure the height contained in the coinbase is correct.
if (this.version >= 2 && prev.isUpgraded(2)) {
cb = bcoin.script.getCoinbaseData(this.txs[0].inputs[0].script);
// Make sure the coinbase is parseable.
if (!cb) {
utils.debug('Block has malformed coinbase: %s', this.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', this.rhash);
return false;
}
}
// Signature validation is now enforced (bip66)
if (!(this.version >= 3 && prev.isUpgraded(3)))
flags.dersig = false;
// CHECKLOCKTIMEVERIFY is now usable (bip65)
if (!(this.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 (this.version >= 8 && prev.isUpgraded(8))
// flags.locktimeMedian = true;
// If we are an ancestor of a checkpoint, we can
// skip the input verification.
if (height < network.checkpoints.lastHeight && !network.checkpoints[height])
flags.scriptChecks = false;
// Get timestamp for tx.isFinal().
ts = flags.locktimeMedian
? prev.getMedianTime()
: this.ts;
// Check all transactions
for (i = 0; i < this.txs.length; i++) {
tx = this.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)', this.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', this.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 (this.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', this.rhash);
return false;
}
// BIP30 - Ensure there are no duplicate txids
// this.node.hasTX(tx.hash('hex'), function(err, has) {
// if (has)
if (this.chain[tx.hash('hex')]) {
// Blocks 91842 and 91880 created duplicate
// txids by using the same exact output script
// and extraNonce.
utils.debug('Block is overwriting txids: %s', this.rhash);
if (!(network.type === 'main' && (height === 91842 || height === 91880)))
return false;
}
// Verify the inputs of every tx (CheckInputs)
if (flags.scriptChecks !== false) {
if (tx.isCoinbase())
continue;
for (j = 0; j < tx.inputs.length; j++) {
input = tx.inputs[j];
// We need the previous output in order
// to verify the script.
if (!input.output)
continue;
// 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 false;
}
// Verify the script
if (!tx.verify(j, true, flags)) {
utils.debug('Block has invalid inputs: %s', this.rhash);
return false;
}
}
}
}
return true;
};
Block.prototype.isGenesis = function isGenesis() { Block.prototype.isGenesis = function isGenesis() {
return this.hash('hex') === network.genesis.hash; return this.hash('hex') === network.genesis.hash;
}; };

View File

@ -39,6 +39,8 @@ function Chain(options) {
this.request = new utils.RequestCache(); this.request = new utils.RequestCache();
this.loading = false; this.loading = false;
this.tip = null; this.tip = null;
this.mempool = options.mempool;
this.blockdb = options.blockdb;
this.orphan = { this.orphan = {
map: {}, map: {},
@ -261,16 +263,16 @@ Chain.prototype._preload = function _preload(callback) {
Chain.prototype._saveBlock = function _saveBlock(block, callback) { Chain.prototype._saveBlock = function _saveBlock(block, callback) {
var self = this; var self = this;
var node = bcoin.node.global;
if (!node) if (!this.blockdb)
return callback(); return callback();
node.block.saveBlock(block, function(err) { this.blockdb.block.saveBlock(block, function(err) {
if (err) if (err)
return callback(err); return callback(err);
node.mempool.addBlock(block); if (self.mempool)
self.mempool.addBlock(block);
return callback(); return callback();
}); });
@ -278,19 +280,19 @@ Chain.prototype._saveBlock = function _saveBlock(block, callback) {
Chain.prototype._removeBlock = function _removeBlock(tip, callback) { Chain.prototype._removeBlock = function _removeBlock(tip, callback) {
var self = this; var self = this;
var node = bcoin.node.global;
if (!node) if (!this.blockdb)
return callback(); return callback();
node.block.removeBlock(tip, function(err, block) { this.blockdb.removeBlock(tip, function(err, block) {
if (err) if (err)
return callback(err); return callback(err);
if (!block) if (!block)
return callback(); return callback();
node.mempool.removeBlock(block); if (self.mempool)
self.mempool.removeBlock(block);
self.emit('reorg block', block.hash('hex')); self.emit('reorg block', block.hash('hex'));
@ -331,10 +333,9 @@ Chain.prototype._verifyContext = function _verifyContext(block, prev, callback)
}; };
Chain.prototype._verify = function _verify(block, prev) { Chain.prototype._verify = function _verify(block, prev) {
// var flags = constants.flags.MANDATORY_VERIFY_FLAGS; var flags = constants.flags.MANDATORY_VERIFY_FLAGS;
var flags = {};
var sigops = 0; var sigops = 0;
var height, ts, i, tx, cb, coinbaseHeight; var height, ts, i, tx, cb, coinbaseHeight, medianTime, locktimeMedian;
// Skip the genesis block // Skip the genesis block
if (block.isGenesis()) if (block.isGenesis())
@ -347,9 +348,10 @@ Chain.prototype._verify = function _verify(block, prev) {
} }
height = prev.height + 1; height = prev.height + 1;
medianTime = prev.getMedianTime();
// Ensure the timestamp is correct // Ensure the timestamp is correct
if (block.ts <= prev.getMedianTime()) { if (block.ts <= medianTime) {
utils.debug('Block time is lower than median: %s', block.rhash); utils.debug('Block time is lower than median: %s', block.rhash);
return false; return false;
} }
@ -393,12 +395,12 @@ Chain.prototype._verify = function _verify(block, prev) {
coinbaseHeight = true; coinbaseHeight = true;
// Signature validation is now enforced (bip66) // Signature validation is now enforced (bip66)
if (!(block.version >= 3 && prev.isUpgraded(3))) if (block.version >= 3 && prev.isUpgraded(3))
flags.dersig = false; flags |= constants.flags.VERIFY_DERSIG;
// CHECKLOCKTIMEVERIFY is now usable (bip65) // CHECKLOCKTIMEVERIFY is now usable (bip65)
if (!(block.version >= 4 && prev.isUpgraded(4))) if (block.version >= 4 && prev.isUpgraded(4))
flags.checklocktimeverify = false; flags |= constants.flags.VERIFY_CHECKLOCKTIMEVERIFY;
// Use nLockTime median past (bip113) // Use nLockTime median past (bip113)
// https://github.com/btcdrak/bips/blob/d4c9a236ecb947866c61aefb868b284498489c2b/bip-0113.mediawiki // https://github.com/btcdrak/bips/blob/d4c9a236ecb947866c61aefb868b284498489c2b/bip-0113.mediawiki
@ -406,7 +408,7 @@ Chain.prototype._verify = function _verify(block, prev) {
// https://gist.github.com/sipa/bf69659f43e763540550 // https://gist.github.com/sipa/bf69659f43e763540550
// http://lists.linuxfoundation.org/pipermail/bitcoin-dev/2015-August/010396.html // http://lists.linuxfoundation.org/pipermail/bitcoin-dev/2015-August/010396.html
// if (block.version >= 8 && prev.isUpgraded(8)) // if (block.version >= 8 && prev.isUpgraded(8))
// flags.locktimeMedian = true; // locktimeMedian = true;
// Can't verify any further when merkleblock or headers. // Can't verify any further when merkleblock or headers.
if (block.subtype !== 'block') if (block.subtype !== 'block')
@ -430,9 +432,7 @@ Chain.prototype._verify = function _verify(block, prev) {
} }
// Get timestamp for tx.isFinal(). // Get timestamp for tx.isFinal().
ts = flags.locktimeMedian ts = locktimeMedian ? medianTime : block.ts;
? prev.getMedianTime()
: block.ts;
// Check all transactions // Check all transactions
for (i = 0; i < block.txs.length; i++) { for (i = 0; i < block.txs.length; i++) {
@ -474,12 +474,11 @@ Chain.prototype._verify = function _verify(block, prev) {
}; };
Chain.prototype._checkDuplicates = function _checkDuplicates(block, prev, callback) { Chain.prototype._checkDuplicates = function _checkDuplicates(block, prev, callback) {
var node = bcoin.node.global;
var height = prev.height + 1; var height = prev.height + 1;
var pending = block.txs.length; var pending = block.txs.length;
var called; var called;
if (!node || block.subtype !== 'block') if (!this.blockdb || block.subtype !== 'block')
return callback(null, true); return callback(null, true);
if (block.isGenesis()) if (block.isGenesis())
@ -499,7 +498,7 @@ Chain.prototype._checkDuplicates = function _checkDuplicates(block, prev, callba
var hash = tx.hash('hex'); var hash = tx.hash('hex');
// BIP30 - Ensure there are no duplicate txids // BIP30 - Ensure there are no duplicate txids
node.block.hasTX(hash, function(err, result) { self.blockdb.hasTX(hash, function(err, result) {
if (called) if (called)
return; return;
@ -522,10 +521,9 @@ Chain.prototype._checkDuplicates = function _checkDuplicates(block, prev, callba
}; };
Chain.prototype._checkInputs = function _checkInputs(block, prev, flags, callback) { Chain.prototype._checkInputs = function _checkInputs(block, prev, flags, callback) {
var node = bcoin.node.global;
var height = prev.height + 1; var height = prev.height + 1;
if (!node || block.subtype !== 'block') if (!this.blockdb || block.subtype !== 'block')
return callback(null, true); return callback(null, true);
if (block.isGenesis()) if (block.isGenesis())
@ -536,7 +534,7 @@ Chain.prototype._checkInputs = function _checkInputs(block, prev, flags, callbac
if (height < network.checkpoints.lastHeight && !network.checkpoints[height]) if (height < network.checkpoints.lastHeight && !network.checkpoints[height])
return callback(null, true); return callback(null, true);
node.block.fillCoins(block.txs, function(err) { this.blockdb.fillCoins(block.txs, function(err) {
var i, j, input, hash; var i, j, input, hash;
if (err) if (err)

View File

@ -52,12 +52,16 @@ ChainBlock.prototype.getProof = function getProof() {
return new bn(1).ushln(256).div(target.addn(1)); return new bn(1).ushln(256).div(target.addn(1));
}; };
ChainBlock.prototype.getChainwork = function() { ChainBlock.prototype.getChainwork = function getChainwork() {
var prev = this.prev; var prev = this.prev;
return (prev ? prev.chainwork : new bn(0)).add(this.getProof()); return (prev ? prev.chainwork : new bn(0)).add(this.getProof());
}; };
ChainBlock.prototype.getMedianTime = function() { ChainBlock.prototype.isGenesis = function isGenesis(version) {
return this.hash === network.genesis.hash;
};
ChainBlock.prototype.getMedianTime = function getMedianTime() {
var entry = this; var entry = this;
var median = []; var median = [];
var timeSpan = constants.block.medianTimespan; var timeSpan = constants.block.medianTimespan;
@ -71,15 +75,15 @@ ChainBlock.prototype.getMedianTime = function() {
return median[median.length / 2 | 0]; return median[median.length / 2 | 0];
}; };
ChainBlock.prototype.isOutdated = function(version) { ChainBlock.prototype.isOutdated = function isOutdated(version) {
return this.isSuperMajority(version, network.block.majorityRejectOutdated); return this.isSuperMajority(version, network.block.majorityRejectOutdated);
}; };
ChainBlock.prototype.isUpgraded = function(version) { ChainBlock.prototype.isUpgraded = function isUpgraded(version) {
return this.isSuperMajority(version, network.block.majorityEnforceUpgrade); return this.isSuperMajority(version, network.block.majorityEnforceUpgrade);
}; };
ChainBlock.prototype.isSuperMajority = function(version, required) { ChainBlock.prototype.isSuperMajority = function isSuperMajority(version, required) {
var entry = this; var entry = this;
var found = 0; var found = 0;
var majorityWindow = network.block.majorityWindow; var majorityWindow = network.block.majorityWindow;
@ -94,7 +98,7 @@ ChainBlock.prototype.isSuperMajority = function(version, required) {
return found >= required; return found >= required;
}; };
ChainBlock.prototype.toJSON = function() { ChainBlock.prototype.toJSON = function toJSON() {
return { return {
hash: this.hash, hash: this.hash,
version: this.version, version: this.version,
@ -107,7 +111,7 @@ ChainBlock.prototype.toJSON = function() {
}; };
}; };
ChainBlock.fromJSON = function(chain, json) { ChainBlock.fromJSON = function fromJSON(chain, json) {
return new ChainBlock(chain, json); return new ChainBlock(chain, json);
}; };

View File

@ -1213,9 +1213,13 @@ function pbkdf2(key, salt, iterations, dkLen) {
if (typeof key === 'string') if (typeof key === 'string')
key = new Buffer(key, 'ascii'); key = new Buffer(key, 'ascii');
else if (Array.isArray(key))
key = new Buffer(key);
if (typeof salt === 'string') if (typeof salt === 'string')
salt = new Buffer(salt, 'ascii'); salt = new Buffer(salt, 'ascii');
else if (Array.isArray(key))
salt = new Buffer(salt);
var DK = new Buffer(dkLen); var DK = new Buffer(dkLen);
var U = new Buffer(hLen); var U = new Buffer(hLen);

View File

@ -19,7 +19,7 @@ var fs = bcoin.fs;
* Mempool * Mempool
*/ */
function Mempool(node, options) { function Mempool(pool, options) {
if (!(this instanceof Mempool)) if (!(this instanceof Mempool))
return new Mempool(pool, options); return new Mempool(pool, options);
@ -27,9 +27,8 @@ function Mempool(node, options) {
options = {}; options = {};
this.options = options; this.options = options;
this.node = node; this.pool = pool;
this.pool = node.pool; this.blockdb = pool.blockdb;
this.block = node.block;
this.txs = {}; this.txs = {};
this.spent = {}; this.spent = {};
@ -38,6 +37,8 @@ function Mempool(node, options) {
this.count = 0; this.count = 0;
this.locked = false; this.locked = false;
Mempool.global = this;
this._init(); this._init();
} }
@ -178,6 +179,7 @@ Mempool.prototype.hasTX = function hasTX(hash) {
Mempool.prototype.add = Mempool.prototype.add =
Mempool.prototype.addTX = function addTX(tx, peer, callback) { Mempool.prototype.addTX = function addTX(tx, peer, callback) {
var self = this; var self = this;
var flags = constants.flags.STANDARD_VERIFY_FLAGS;
var hash = tx.hash('hex'); var hash = tx.hash('hex');
assert(tx.ts === 0); assert(tx.ts === 0);
@ -201,7 +203,7 @@ Mempool.prototype.addTX = function addTX(tx, peer, callback) {
this._lockTX(tx); this._lockTX(tx);
this.block.fillCoin(tx, function(err) { this.blockdb.fillCoin(tx, function(err) {
var i, input, dup, height, ts, priority; var i, input, dup, height, ts, priority;
self._unlockTX(tx); self._unlockTX(tx);
@ -294,7 +296,7 @@ Mempool.prototype.addTX = function addTX(tx, peer, callback) {
} }
} }
if (!tx.verify(null, true)) { if (!tx.verify(null, true, flags)) {
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(),
@ -340,6 +342,8 @@ Mempool.prototype.addTX = function addTX(tx, peer, callback) {
self.txs[hash] = tx; self.txs[hash] = tx;
self.count++; self.count++;
self.size += tx.getSize(); self.size += tx.getSize();
self.emit('tx', tx);
}); });
}; };
@ -425,6 +429,7 @@ Mempool.prototype.removeTX = function removeTX(hash, callback) {
delete this.txs[hash]; delete this.txs[hash];
this.count--; this.count--;
this.size -= tx.getSize(); this.size -= tx.getSize();
this.emit('remove tx', tx);
}; };
// Need to lock the mempool when // Need to lock the mempool when

View File

@ -137,46 +137,47 @@ Miner.prototype.addBlock = function addBlock(block) {
}; };
Miner.prototype.addTX = function addTX(tx) { Miner.prototype.addTX = function addTX(tx) {
var full, ts; var flags, height, ts;
full = this.index.inputs.every(function(input) { flags = constants.flags.MANDATORY_VERIFY_FLAGS
return !!input.output; | constants.flags.VERIFY_DERSIG
}); | constants.flags.VERIFY_CHECKLOCKTIMEVERIFY;
// Cannot calculate fee if we don't have the prev_out. // Cannot calculate fee if we don't have the prev_out.
// Could possibly just burn some coins. // Could possibly just burn some coins.
if (this.options.burn === false) { if (this.options.burn === false) {
if (!full) if (!tx.hasPrevout())
return; return false;
} }
// Ignore if it's already in a block // Ignore if it's already in a block
if (tx.height !== -1) if (tx.height !== -1)
return; return false;
if (!tx.verify(null, true)) if (!tx.verify(null, true, flags))
return; return false;
if (tx.isCoinbase()) if (tx.isCoinbase())
return; return false;
// Get timestamp for tx.isFinal() - bip113 // Get timestamp for tx.isFinal() - bip113
ts = this.block.version === 8 height = this.last.height + 1;
ts = this.block.version >= 8
? this.last.getMedianTime() ? this.last.getMedianTime()
: this.block.ts; : this.block.ts;
if (!tx.isFinal(this.last.height + 1, ts)) if (!tx.isFinal(height, ts))
return; return false;
// Deliver me from the block size debate, please // Deliver me from the block size debate, please
if (this.block.getSize() + tx.getSize() > constants.blocks.maxSize) if (this.block.getSize() + tx.getSize() > constants.blocks.maxSize)
return; return false;
// Add the tx to our block // Add the tx to our block
this.block.txs.push(tx); this.block.txs.push(tx);
// Calculate our new reward fee // Calculate our new reward fee
if (full) if (tx.hasPrevout())
this.fee.iadd(tx.getFee()); this.fee.iadd(tx.getFee());
// Update coinbase value // Update coinbase value

View File

@ -35,7 +35,7 @@ function Node(options) {
if (this.options.network) if (this.options.network)
network.set(this.options.network); network.set(this.options.network);
this.block = null; this.blockdb = null;
this.mempool = null; this.mempool = null;
this.pool = null; this.pool = null;
this.chain = null; this.chain = null;
@ -50,80 +50,27 @@ inherits(Node, EventEmitter);
Node.prototype._init = function _init() { Node.prototype._init = function _init() {
var self = this; var self = this;
this.blockdb = new bcoin.blockdb(this.options.blockdb);
this.mempool = new bcoin.mempool(this, this.options.mempool);
if (!this.options.pool) if (!this.options.pool)
this.options.pool = {}; this.options.pool = {};
this.options.pool.type = 'full'; this.options.pool.spv = false;
this.options.pool.blockdb = this.blockdb;
this.options.pool.mempool = this.mempool;
this.block = new bcoin.blockdb(this.options.block);
this.mempool = new bcoin.mempool(this, this.options.mempool);
this.pool = new bcoin.pool(this.options.pool); this.pool = new bcoin.pool(this.options.pool);
this.chain = this.pool.chain; this.chain = this.pool.chain;
if (0)
this.pool.on('block', function(block, peer) {
self.mempool.addBlock(block);
});
if (0)
this.pool.on('block', function(block, peer) {
self.block.saveBlock(block, function(err) {
if (err)
throw err;
self.mempool.addBlock(block);
if (0)
var hash = block.txs[0].hash('hex');
if (0)
self.block.getTX(hash, function(err, tx) {
if (err) throw err;
utils.print(tx);
});
if (0)
self.block.getCoin(hash, 0, function(err, tx) {
if (err) throw err;
utils.print(tx);
});
});
});
this.mempool.on('error', function(err) { this.mempool.on('error', function(err) {
self.emit('error', err); self.emit('error', err);
}); });
this.chain.on('error', function(err) {
self.emit('error', err);
});
this.pool.on('error', function(err) { this.pool.on('error', function(err) {
self.emit('error', err); self.emit('error', err);
}); });
this.pool.on('fork', function(data) {
if (data.block)
self.mempool.removeBlock(block);
});
if (0)
this.pool.on('fork', function(a, b) {
[a, b].forEach(function(hash) {
self.block.removeBlock(hash, function(err, block) {
if (err)
throw err;
if (!block)
return;
self.mempool.removeBlock(block);
});
});
});
this.pool.on('tx', function(tx, peer) {
assert(tx.ts === 0);
self.mempool.addTX(tx, peer);
});
this.pool.startSync(); this.pool.startSync();
}; };
@ -140,12 +87,12 @@ Node.prototype.getCoin = function getCoin(hash, index, callback) {
if (this.mempool.isSpent(hash, index)) if (this.mempool.isSpent(hash, index))
return callback(null, null); return callback(null, null);
this.block.getCoin(hash, index, function(err, coin) { this.blockdb.getCoin(hash, index, function(err, coin) {
if (err) if (err)
return callback(err); return callback(err);
if (!coin) if (!coin)
return; return callback();
return callback(null, coin); return callback(null, coin);
}); });
@ -159,7 +106,7 @@ Node.prototype.getCoinByAddress = function getCoinsByAddress(addresses, callback
mempool = this.mempool.getCoinsByAddress(addresses); mempool = this.mempool.getCoinsByAddress(addresses);
this.block.getCoinsByAddress(addresses, function(err, coins) { this.blockdb.getCoinsByAddress(addresses, function(err, coins) {
if (err) if (err)
return callback(err); return callback(err);
@ -181,10 +128,13 @@ Node.prototype.getTX = function getTX(hash, callback) {
if (tx) if (tx)
return callback(null, tx); return callback(null, tx);
this.block.getTX(hash, function(err, tx) { this.blockdb.getTX(hash, function(err, tx) {
if (err) if (err)
return callback(err); return callback(err);
if (!tx)
return callback();
return callback(null, tx); return callback(null, tx);
}); });
}; };
@ -197,7 +147,7 @@ Node.prototype.isSpent = function isSpent(hash, index, callback) {
if (this.mempool.isSpent(hash, index)) if (this.mempool.isSpent(hash, index))
return callback(null, true); return callback(null, true);
this.block.isSpent(hash, index, callback); this.blockdb.isSpent(hash, index, callback);
}; };
Node.prototype.getTXByAddress = function getTXByAddress(addresses, callback) { Node.prototype.getTXByAddress = function getTXByAddress(addresses, callback) {
@ -208,7 +158,7 @@ Node.prototype.getTXByAddress = function getTXByAddress(addresses, callback) {
mempool = this.mempool.getTXByAddress(addresses); mempool = this.mempool.getTXByAddress(addresses);
this.block.getTXByAddress(addresses, function(err, txs) { this.blockdb.getTXByAddress(addresses, function(err, txs) {
if (err) if (err)
return callback(err); return callback(err);
@ -218,16 +168,20 @@ Node.prototype.getTXByAddress = function getTXByAddress(addresses, callback) {
Node.prototype.fillCoin = function fillCoin(tx, callback) { Node.prototype.fillCoin = function fillCoin(tx, callback) {
callback = utils.asyncify(callback); callback = utils.asyncify(callback);
if (this.mempool.fillCoin(tx)) if (this.mempool.fillCoin(tx))
return callback(); return callback();
this.block.fillCoin(tx, callback);
this.blockdb.fillCoin(tx, callback);
}; };
Node.prototype.fillTX = function fillTX(tx, callback) { Node.prototype.fillTX = function fillTX(tx, callback) {
callback = utils.asyncify(callback); callback = utils.asyncify(callback);
if (this.mempool.fillTX(tx)) if (this.mempool.fillTX(tx))
return callback(); return callback();
this.block.fillTX(tx, callback);
this.blockdb.fillTX(tx, callback);
}; };
/** /**

View File

@ -144,13 +144,11 @@ Peer.prototype._init = function init() {
self.pool.setMisbehavior(self, 100); self.pool.setMisbehavior(self, 100);
}); });
if (this.pool.options.fullNode) { this.once('version', function() {
this.once('version', function() { utils.debug(
utils.debug( 'Sent version (%s): height=%s',
'Sent version (%s): height=%s', self.host, this.pool.chain.height());
self.host, this.pool.chain.height()); });
});
}
this._ping.timer = setInterval(function() { this._ping.timer = setInterval(function() {
self.challenge = utils.nonce(); self.challenge = utils.nonce();
@ -269,7 +267,7 @@ Peer.prototype.broadcast = function broadcast(items) {
}; };
Peer.prototype.updateWatch = function updateWatch() { Peer.prototype.updateWatch = function updateWatch() {
if (this.pool.options.fullNode) if (!this.pool.options.spv)
return; return;
if (this.ack) if (this.ack)

View File

@ -32,24 +32,24 @@ function Pool(options) {
this.options = options; this.options = options;
if (this.options.debug) if (options.debug)
bcoin.debug = this.options.debug; bcoin.debug = this.options.debug;
if (this.options.network) if (options.network)
network.set(this.options.network); network.set(options.network);
this.options.fullNode = !!this.options.fullNode; options.spv = options.spv !== false;
if (options.type === 'spv') if (options.type === 'spv')
this.options.fullNode = false; options.spv = true;
else if (options.type === 'full') else if (options.type === 'full')
this.options.fullNode = true; options.spv = false;
this.options.headers = this.options.headers; options.headers = options.headers;
this.options.multiplePeers = this.options.multiplePeers; options.multiplePeers = options.multiplePeers;
this.options.relay = this.options.relay == null options.relay = options.relay == null
? (this.options.fullNode ? true : false) ? (!options.spv ? true : false)
: this.options.relay; : options.relay;
this.originalSeeds = (options.seeds || network.seeds).map(utils.parseHost); this.originalSeeds = (options.seeds || network.seeds).map(utils.parseHost);
this.setSeeds([]); this.setSeeds([]);
@ -58,20 +58,23 @@ function Pool(options) {
this.destroyed = false; this.destroyed = false;
this.size = options.size || 32; this.size = options.size || 32;
if (!this.options.fullNode) { this.blockdb = options.blockdb;
if (this.options.headers == null) this.mempool = options.mempool;
this.options.headers = true;
if (this.options.multiplePeers == null) if (options.spv) {
this.options.multiplePeers = true; if (options.headers == null)
options.headers = true;
if (options.multiplePeers == null)
options.multiplePeers = true;
} else { } else {
if (this.options.headers == null) if (options.headers == null)
this.options.headers = false; options.headers = false;
if (this.options.multiplePeers == null) if (options.multiplePeers == null)
this.options.multiplePeers = false; options.multiplePeers = false;
} }
if (!this.options.headers) if (!options.headers)
this.options.multiplePeers = false; options.multiplePeers = false;
this.syncing = false; this.syncing = false;
this.synced = false; this.synced = false;
@ -84,9 +87,11 @@ function Pool(options) {
this.requestTimeout = options.requestTimeout || 600000; this.requestTimeout = options.requestTimeout || 600000;
this.chain = new bcoin.chain({ this.chain = new bcoin.chain({
fullNode: this.options.fullNode, spv: options.spv,
multiplePeers: this.options.multiplePeers, multiplePeers: options.multiplePeers,
preload: this.options.preload preload: options.preload,
blockdb: options.blockdb,
mempool: options.mempool
}); });
this.watchMap = {}; this.watchMap = {};
@ -115,7 +120,7 @@ function Pool(options) {
this.block = { this.block = {
bestHeight: 0, bestHeight: 0,
bestHash: null, bestHash: null,
type: this.options.fullNode ? 'block' : 'filtered', type: !options.spv ? 'block' : 'filtered',
invalid: {} invalid: {}
}; };
@ -133,10 +138,6 @@ function Pool(options) {
this.validate = { this.validate = {
// 5 days scan delta for obtaining TXs // 5 days scan delta for obtaining TXs
delta: 5 * 24 * 3600, delta: 5 * 24 * 3600,
// Minimum verification depth
minDepth: options.minValidateDepth || 0,
// getTX map // getTX map
map: {} map: {}
}; };
@ -148,7 +149,7 @@ function Pool(options) {
}; };
// Added and watched wallets // Added and watched wallets
this.options.wallets = this.options.wallets || []; options.wallets = options.wallets || [];
this.wallets = []; this.wallets = [];
Pool.global = this; Pool.global = this;
@ -615,7 +616,7 @@ Pool.prototype._handleBlock = function _handleBlock(block, peer, callback) {
requested = self._response(block); requested = self._response(block);
// Emulate BIP37: emit all the filtered transactions. // Emulate BIP37: emit all the filtered transactions.
if (self.options.fullNode && self.listeners('watched').length > 0) { if (!self.options.spv && self.listeners('watched').length > 0) {
block.txs.forEach(function(tx) { block.txs.forEach(function(tx) {
if (self.isWatched(tx)) if (self.isWatched(tx))
self.emit('watched', tx, peer); self.emit('watched', tx, peer);
@ -857,22 +858,35 @@ Pool.prototype._handleTX = function _handleTX(tx, peer, callback) {
callback = utils.asyncify(callback); callback = utils.asyncify(callback);
this._prehandleTX(tx, peer, function(err) { function addMempool(tx, peer, callback) {
var requested, added; if (!self.mempool)
return callback();
if (tx.ts !== 0)
return callback();
self.mempool.addTX(tx, peer, callback);
}
this._prehandleTX(tx, peer, function(err) {
if (err) if (err)
return callback(err); return callback(err);
requested = self._response(tx); addMempool(tx, peer, function(err) {
added = self._addTX(tx, 1); var requested, added;
if (added || tx.block) if (err)
self.emit('tx', tx, peer); utils.debug('Mempool error: %s', err.message);
if (!self.options.fullNode && tx.block) requested = self._response(tx);
self.emit('watched', tx, peer); added = self._addTX(tx, 1);
return callback(); if (added || tx.block)
self.emit('tx', tx, peer);
if (self.options.spv && tx.block)
self.emit('watched', tx, peer);
return callback();
});
}); });
}; };
@ -1232,7 +1246,7 @@ Pool.prototype.addWallet = function addWallet(w) {
self.sendTX(tx); self.sendTX(tx);
}); });
if (self.options.fullNode) if (!self.options.spv)
return; return;
if (self._pendingSearch) if (self._pendingSearch)
@ -1323,7 +1337,7 @@ Pool.prototype.searchWallet = function(w, h) {
assert(!this.loading); assert(!this.loading);
if (this.options.fullNode) if (!this.options.spv)
return; return;
if (w == null) { if (w == null) {
@ -1393,7 +1407,7 @@ Pool.prototype.search = function search(id, range, e) {
assert(!this.loading); assert(!this.loading);
if (this.options.fullNode) if (!this.options.spv)
return; return;
if (typeof e === 'function') { if (typeof e === 'function') {
@ -1570,7 +1584,7 @@ Pool.prototype.getTX = function getTX(hash, range, cb) {
if (!this.peers.load) if (!this.peers.load)
return setTimeout(this.getBlock.bind(this, hash, cb), 1000); return setTimeout(this.getBlock.bind(this, hash, cb), 1000);
if (this.options.fullNode) if (!this.options.spv)
return cb(new Error('Cannot get tx with full node')); return cb(new Error('Cannot get tx with full node'));
hash = utils.toHex(hash); hash = utils.toHex(hash);

View File

@ -272,3 +272,33 @@ exports.userAgent = '/bcoin:' + exports.userVersion + '/';
exports.banTime = 24 * 60 * 60; exports.banTime = 24 * 60 * 60;
exports.banScore = 100; exports.banScore = 100;
// Script and locktime flags
exports.flags = {
VERIFY_NONE: 0,
VERIFY_P2SH: (1 << 0),
VERIFY_STRICTENC: (1 << 1),
VERIFY_DERSIG: (1 << 2),
VERIFY_LOW_S: (1 << 3),
VERIFY_NULLDUMMY: (1 << 4),
VERIFY_SIGPUSHONLY: (1 << 5),
VERIFY_MINIMALDATA: (1 << 6),
VERIFY_DISCOURAGE_UPGRADABLE_NOPS: (1 << 7),
VERIFY_CLEANSTACK: (1 << 8),
VERIFY_CHECKLOCKTIMEVERIFY: (1 << 9)
};
// Block validation
exports.flags.MANDATORY_VERIFY_FLAGS = exports.flags.VERIFY_P2SH;
// Mempool validation
exports.flags.STANDARD_VERIFY_FLAGS =
exports.flags.MANDATORY_VERIFY_FLAGS
| exports.flags.VERIFY_DERSIG
| exports.flags.VERIFY_STRICTENC
| exports.flags.VERIFY_MINIMALDATA
| exports.flags.VERIFY_NULLDUMMY
| exports.flags.VERIFY_DISCOURAGE_UPGRADABLE_NOPS
| exports.flags.VERIFY_CLEANSTACK
| exports.flags.VERIFY_CHECKLOCKTIMEVERIFY
| exports.flags.VERIFY_LOW_S;

View File

@ -198,10 +198,10 @@ script.verify = function verify(input, output, tx, i, flags) {
var copy, res, redeem; var copy, res, redeem;
var stack = []; var stack = [];
if (!flags) if (flags == null)
flags = {}; flags = constants.flags.STANDARD_VERIFY_FLAGS;
if (flags.sigpushonly !== false) { if (flags & constants.flags.VERIFY_SIGPUSHONLY) {
if (!script.isPushOnly(input)) if (!script.isPushOnly(input))
return false; return false;
} }
@ -210,7 +210,7 @@ script.verify = function verify(input, output, tx, i, flags) {
script.execute(input, stack, tx, i, flags); script.execute(input, stack, tx, i, flags);
// Copy the stack for P2SH // Copy the stack for P2SH
if (flags.verifyp2sh !== false) if (flags & constants.flags.VERIFY_P2SH)
copy = stack.slice(); copy = stack.slice();
// Execute the previous output script // Execute the previous output script
@ -221,7 +221,7 @@ script.verify = function verify(input, output, tx, i, flags) {
return false; return false;
// If the script is P2SH, execute the real output script // If the script is P2SH, execute the real output script
if (flags.verifyp2sh !== false && script.isScripthash(output)) { if ((flags & constants.flags.VERIFY_P2SH) && script.isScripthash(output)) {
// P2SH can only have push ops in the scriptSig // P2SH can only have push ops in the scriptSig
if (!script.isPushOnly(input)) if (!script.isPushOnly(input))
return false; return false;
@ -250,7 +250,7 @@ script.verify = function verify(input, output, tx, i, flags) {
} }
// Ensure there is nothing left on the stack // Ensure there is nothing left on the stack
if (flags.cleanstack !== false) { if (flags & constants.flags.VERIFY_CLEANSTACK) {
if (stack.length !== 0) if (stack.length !== 0)
return false; return false;
} }
@ -370,8 +370,8 @@ script._next = function _next(to, s, pc) {
script.execute = function execute(data, stack, tx, index, flags, recurse) { script.execute = function execute(data, stack, tx, index, flags, recurse) {
var s = data.slice(); var s = data.slice();
if (!flags) if (flags == null)
flags = {}; flags = constants.flags.STANDARD_VERIFY_FLAGS;
if (s.length > constants.script.maxOps) if (s.length > constants.script.maxOps)
return false; return false;
@ -418,7 +418,7 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
case 'nop8': case 'nop8':
case 'nop9': case 'nop9':
case 'nop10': { case 'nop10': {
if (flags.discourage_nops !== false) if (flags & constants.flags.VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
return false; return false;
break; break;
} }
@ -908,7 +908,7 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
val = stack.pop(); val = stack.pop();
if (flags.verifynulldummy !== false) { if (flags & constants.flags.VERIFY_NULLDUMMY) {
if (!script.isDummy(val)) if (!script.isDummy(val))
return false; return false;
} }
@ -925,8 +925,8 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
} }
case 'checklocktimeverify': { case 'checklocktimeverify': {
// OP_CHECKLOCKTIMEVERIFY = OP_NOP2 // OP_CHECKLOCKTIMEVERIFY = OP_NOP2
if (flags.checklocktimeverify === false) { if (!(flags & constants.flags.VERIFY_CHECKLOCKTIMEVERIFY)) {
if (flags.discourage_nops !== false) if (flags & constants.flags.VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
return false; return false;
break; break;
} }
@ -965,8 +965,8 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
} }
case 'nop1': { case 'nop1': {
// OP_EVAL = OP_NOP1 // OP_EVAL = OP_NOP1
if (flags['eval'] !== true) { if (!script.allowEval) {
if (flags.discourage_nops !== false) if (flags & constants.flags.VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
return false; return false;
break; break;
} }
@ -1126,10 +1126,13 @@ script.removeData = function removeData(s, data) {
}; };
script.checkPush = function checkPush(op, value, flags) { script.checkPush = function checkPush(op, value, flags) {
if (!flags) if (flags == null)
flags = {}; flags = constants.flags.STANDARD_VERIFY_FLAGS;
if (flags.minimaldata === false) // Disabled for now.
return true;
if (!(flags & constants.flags.VERIFY_MINIMALDATA))
return true; return true;
if (!op.pushdata) if (!op.pushdata)
@ -2038,13 +2041,13 @@ script.isData = function isData(data) {
}; };
script.isValidKey = function isValidKey(key, flags) { script.isValidKey = function isValidKey(key, flags) {
if (!flags) if (flags == null)
flags = {}; flags = constants.flags.STANDARD_VERIFY_FLAGS;
if (!utils.isBuffer(key)) if (!utils.isBuffer(key))
return false; return false;
if (flags.strictenc !== false) { if (flags & constants.flags.VERIFY_STRICTENC) {
if (!script.isKeyEncoding(key)) if (!script.isKeyEncoding(key))
return false; return false;
} }
@ -2073,8 +2076,8 @@ script.isKeyEncoding = function isKeyEncoding(key) {
}; };
script.isValidSignature = function isValidSignature(sig, flags) { script.isValidSignature = function isValidSignature(sig, flags) {
if (!flags) if (flags == null)
flags = {}; flags = constants.flags.STANDARD_VERIFY_FLAGS;
if (!utils.isBuffer(sig)) if (!utils.isBuffer(sig))
return false; return false;
@ -2083,19 +2086,19 @@ script.isValidSignature = function isValidSignature(sig, flags) {
if (sig.length === 0) if (sig.length === 0)
return true; return true;
if (flags.dersig !== false if ((flags & constants.flags.VERIFY_DERSIG)
|| flags.low_s !== false || (flags & constants.flags.VERIFY_LOW_S)
|| flags.strictenc !== false) { || (flags & constants.flags.VERIFY_STRICTENC)) {
if (!script.isSignatureEncoding(sig)) if (!script.isSignatureEncoding(sig))
return false; return false;
} }
if (flags.low_s !== false) { if (flags & constants.flags.VERIFY_LOW_S) {
if (!script.isLowDER(sig)) if (!script.isLowDER(sig))
return false; return false;
} }
if (flags.strictenc !== false) { if (flags & constants.flags.VERIFY_STRICTENC) {
if (!script.isHashType(sig)) if (!script.isHashType(sig))
return false; return false;
} }

View File

@ -809,13 +809,6 @@ TX.prototype.signatureHash = function signatureHash(index, s, type) {
if (typeof index !== 'number') if (typeof index !== 'number')
index = this.inputs.indexOf(index); index = this.inputs.indexOf(index);
if (!Array.isArray(s)) {
type = s;
s = this.inputs[index].output.script;
if (bcoin.script.isScripthash(s))
s = bcoin.script.getRedeem(this.inputs[index].script);
}
if (typeof type === 'string') if (typeof type === 'string')
type = constants.hashType[type]; type = constants.hashType[type];