more refactoring.
This commit is contained in:
parent
ab610d6fa5
commit
997f0acbb0
@ -299,194 +299,6 @@ Block.prototype._verify = function _verify() {
|
||||
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() {
|
||||
return this.hash('hex') === network.genesis.hash;
|
||||
};
|
||||
|
||||
@ -39,6 +39,8 @@ function Chain(options) {
|
||||
this.request = new utils.RequestCache();
|
||||
this.loading = false;
|
||||
this.tip = null;
|
||||
this.mempool = options.mempool;
|
||||
this.blockdb = options.blockdb;
|
||||
|
||||
this.orphan = {
|
||||
map: {},
|
||||
@ -261,16 +263,16 @@ Chain.prototype._preload = function _preload(callback) {
|
||||
|
||||
Chain.prototype._saveBlock = function _saveBlock(block, callback) {
|
||||
var self = this;
|
||||
var node = bcoin.node.global;
|
||||
|
||||
if (!node)
|
||||
if (!this.blockdb)
|
||||
return callback();
|
||||
|
||||
node.block.saveBlock(block, function(err) {
|
||||
this.blockdb.block.saveBlock(block, function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
node.mempool.addBlock(block);
|
||||
if (self.mempool)
|
||||
self.mempool.addBlock(block);
|
||||
|
||||
return callback();
|
||||
});
|
||||
@ -278,19 +280,19 @@ Chain.prototype._saveBlock = function _saveBlock(block, callback) {
|
||||
|
||||
Chain.prototype._removeBlock = function _removeBlock(tip, callback) {
|
||||
var self = this;
|
||||
var node = bcoin.node.global;
|
||||
|
||||
if (!node)
|
||||
if (!this.blockdb)
|
||||
return callback();
|
||||
|
||||
node.block.removeBlock(tip, function(err, block) {
|
||||
this.blockdb.removeBlock(tip, function(err, block) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (!block)
|
||||
return callback();
|
||||
|
||||
node.mempool.removeBlock(block);
|
||||
if (self.mempool)
|
||||
self.mempool.removeBlock(block);
|
||||
|
||||
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) {
|
||||
// var flags = constants.flags.MANDATORY_VERIFY_FLAGS;
|
||||
var flags = {};
|
||||
var flags = constants.flags.MANDATORY_VERIFY_FLAGS;
|
||||
var sigops = 0;
|
||||
var height, ts, i, tx, cb, coinbaseHeight;
|
||||
var height, ts, i, tx, cb, coinbaseHeight, medianTime, locktimeMedian;
|
||||
|
||||
// Skip the genesis block
|
||||
if (block.isGenesis())
|
||||
@ -347,9 +348,10 @@ Chain.prototype._verify = function _verify(block, prev) {
|
||||
}
|
||||
|
||||
height = prev.height + 1;
|
||||
medianTime = prev.getMedianTime();
|
||||
|
||||
// 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);
|
||||
return false;
|
||||
}
|
||||
@ -393,12 +395,12 @@ Chain.prototype._verify = function _verify(block, prev) {
|
||||
coinbaseHeight = true;
|
||||
|
||||
// Signature validation is now enforced (bip66)
|
||||
if (!(block.version >= 3 && prev.isUpgraded(3)))
|
||||
flags.dersig = false;
|
||||
if (block.version >= 3 && prev.isUpgraded(3))
|
||||
flags |= constants.flags.VERIFY_DERSIG;
|
||||
|
||||
// CHECKLOCKTIMEVERIFY is now usable (bip65)
|
||||
if (!(block.version >= 4 && prev.isUpgraded(4)))
|
||||
flags.checklocktimeverify = false;
|
||||
if (block.version >= 4 && prev.isUpgraded(4))
|
||||
flags |= constants.flags.VERIFY_CHECKLOCKTIMEVERIFY;
|
||||
|
||||
// Use nLockTime median past (bip113)
|
||||
// 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
|
||||
// http://lists.linuxfoundation.org/pipermail/bitcoin-dev/2015-August/010396.html
|
||||
// if (block.version >= 8 && prev.isUpgraded(8))
|
||||
// flags.locktimeMedian = true;
|
||||
// locktimeMedian = true;
|
||||
|
||||
// Can't verify any further when merkleblock or headers.
|
||||
if (block.subtype !== 'block')
|
||||
@ -430,9 +432,7 @@ Chain.prototype._verify = function _verify(block, prev) {
|
||||
}
|
||||
|
||||
// Get timestamp for tx.isFinal().
|
||||
ts = flags.locktimeMedian
|
||||
? prev.getMedianTime()
|
||||
: block.ts;
|
||||
ts = locktimeMedian ? medianTime : block.ts;
|
||||
|
||||
// Check all transactions
|
||||
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) {
|
||||
var node = bcoin.node.global;
|
||||
var height = prev.height + 1;
|
||||
var pending = block.txs.length;
|
||||
var called;
|
||||
|
||||
if (!node || block.subtype !== 'block')
|
||||
if (!this.blockdb || block.subtype !== 'block')
|
||||
return callback(null, true);
|
||||
|
||||
if (block.isGenesis())
|
||||
@ -499,7 +498,7 @@ Chain.prototype._checkDuplicates = function _checkDuplicates(block, prev, callba
|
||||
var hash = tx.hash('hex');
|
||||
|
||||
// BIP30 - Ensure there are no duplicate txids
|
||||
node.block.hasTX(hash, function(err, result) {
|
||||
self.blockdb.hasTX(hash, function(err, result) {
|
||||
if (called)
|
||||
return;
|
||||
|
||||
@ -522,10 +521,9 @@ Chain.prototype._checkDuplicates = function _checkDuplicates(block, prev, callba
|
||||
};
|
||||
|
||||
Chain.prototype._checkInputs = function _checkInputs(block, prev, flags, callback) {
|
||||
var node = bcoin.node.global;
|
||||
var height = prev.height + 1;
|
||||
|
||||
if (!node || block.subtype !== 'block')
|
||||
if (!this.blockdb || block.subtype !== 'block')
|
||||
return callback(null, true);
|
||||
|
||||
if (block.isGenesis())
|
||||
@ -536,7 +534,7 @@ Chain.prototype._checkInputs = function _checkInputs(block, prev, flags, callbac
|
||||
if (height < network.checkpoints.lastHeight && !network.checkpoints[height])
|
||||
return callback(null, true);
|
||||
|
||||
node.block.fillCoins(block.txs, function(err) {
|
||||
this.blockdb.fillCoins(block.txs, function(err) {
|
||||
var i, j, input, hash;
|
||||
|
||||
if (err)
|
||||
|
||||
@ -52,12 +52,16 @@ ChainBlock.prototype.getProof = function getProof() {
|
||||
return new bn(1).ushln(256).div(target.addn(1));
|
||||
};
|
||||
|
||||
ChainBlock.prototype.getChainwork = function() {
|
||||
ChainBlock.prototype.getChainwork = function getChainwork() {
|
||||
var prev = this.prev;
|
||||
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 median = [];
|
||||
var timeSpan = constants.block.medianTimespan;
|
||||
@ -71,15 +75,15 @@ ChainBlock.prototype.getMedianTime = function() {
|
||||
return median[median.length / 2 | 0];
|
||||
};
|
||||
|
||||
ChainBlock.prototype.isOutdated = function(version) {
|
||||
ChainBlock.prototype.isOutdated = function isOutdated(version) {
|
||||
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);
|
||||
};
|
||||
|
||||
ChainBlock.prototype.isSuperMajority = function(version, required) {
|
||||
ChainBlock.prototype.isSuperMajority = function isSuperMajority(version, required) {
|
||||
var entry = this;
|
||||
var found = 0;
|
||||
var majorityWindow = network.block.majorityWindow;
|
||||
@ -94,7 +98,7 @@ ChainBlock.prototype.isSuperMajority = function(version, required) {
|
||||
return found >= required;
|
||||
};
|
||||
|
||||
ChainBlock.prototype.toJSON = function() {
|
||||
ChainBlock.prototype.toJSON = function toJSON() {
|
||||
return {
|
||||
hash: this.hash,
|
||||
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);
|
||||
};
|
||||
|
||||
|
||||
@ -1213,9 +1213,13 @@ function pbkdf2(key, salt, iterations, dkLen) {
|
||||
|
||||
if (typeof key === 'string')
|
||||
key = new Buffer(key, 'ascii');
|
||||
else if (Array.isArray(key))
|
||||
key = new Buffer(key);
|
||||
|
||||
if (typeof salt === 'string')
|
||||
salt = new Buffer(salt, 'ascii');
|
||||
else if (Array.isArray(key))
|
||||
salt = new Buffer(salt);
|
||||
|
||||
var DK = new Buffer(dkLen);
|
||||
var U = new Buffer(hLen);
|
||||
|
||||
@ -19,7 +19,7 @@ var fs = bcoin.fs;
|
||||
* Mempool
|
||||
*/
|
||||
|
||||
function Mempool(node, options) {
|
||||
function Mempool(pool, options) {
|
||||
if (!(this instanceof Mempool))
|
||||
return new Mempool(pool, options);
|
||||
|
||||
@ -27,9 +27,8 @@ function Mempool(node, options) {
|
||||
options = {};
|
||||
|
||||
this.options = options;
|
||||
this.node = node;
|
||||
this.pool = node.pool;
|
||||
this.block = node.block;
|
||||
this.pool = pool;
|
||||
this.blockdb = pool.blockdb;
|
||||
|
||||
this.txs = {};
|
||||
this.spent = {};
|
||||
@ -38,6 +37,8 @@ function Mempool(node, options) {
|
||||
this.count = 0;
|
||||
this.locked = false;
|
||||
|
||||
Mempool.global = this;
|
||||
|
||||
this._init();
|
||||
}
|
||||
|
||||
@ -178,6 +179,7 @@ Mempool.prototype.hasTX = function hasTX(hash) {
|
||||
Mempool.prototype.add =
|
||||
Mempool.prototype.addTX = function addTX(tx, peer, callback) {
|
||||
var self = this;
|
||||
var flags = constants.flags.STANDARD_VERIFY_FLAGS;
|
||||
var hash = tx.hash('hex');
|
||||
|
||||
assert(tx.ts === 0);
|
||||
@ -201,7 +203,7 @@ Mempool.prototype.addTX = function addTX(tx, peer, callback) {
|
||||
|
||||
this._lockTX(tx);
|
||||
|
||||
this.block.fillCoin(tx, function(err) {
|
||||
this.blockdb.fillCoin(tx, function(err) {
|
||||
var i, input, dup, height, ts, priority;
|
||||
|
||||
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.'));
|
||||
peer.reject({
|
||||
data: tx.hash(),
|
||||
@ -340,6 +342,8 @@ Mempool.prototype.addTX = function addTX(tx, peer, callback) {
|
||||
self.txs[hash] = tx;
|
||||
self.count++;
|
||||
self.size += tx.getSize();
|
||||
|
||||
self.emit('tx', tx);
|
||||
});
|
||||
};
|
||||
|
||||
@ -425,6 +429,7 @@ Mempool.prototype.removeTX = function removeTX(hash, callback) {
|
||||
delete this.txs[hash];
|
||||
this.count--;
|
||||
this.size -= tx.getSize();
|
||||
this.emit('remove tx', tx);
|
||||
};
|
||||
|
||||
// Need to lock the mempool when
|
||||
|
||||
@ -137,46 +137,47 @@ Miner.prototype.addBlock = function addBlock(block) {
|
||||
};
|
||||
|
||||
Miner.prototype.addTX = function addTX(tx) {
|
||||
var full, ts;
|
||||
var flags, height, ts;
|
||||
|
||||
full = this.index.inputs.every(function(input) {
|
||||
return !!input.output;
|
||||
});
|
||||
flags = constants.flags.MANDATORY_VERIFY_FLAGS
|
||||
| constants.flags.VERIFY_DERSIG
|
||||
| constants.flags.VERIFY_CHECKLOCKTIMEVERIFY;
|
||||
|
||||
// Cannot calculate fee if we don't have the prev_out.
|
||||
// Could possibly just burn some coins.
|
||||
if (this.options.burn === false) {
|
||||
if (!full)
|
||||
return;
|
||||
if (!tx.hasPrevout())
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ignore if it's already in a block
|
||||
if (tx.height !== -1)
|
||||
return;
|
||||
return false;
|
||||
|
||||
if (!tx.verify(null, true))
|
||||
return;
|
||||
if (!tx.verify(null, true, flags))
|
||||
return false;
|
||||
|
||||
if (tx.isCoinbase())
|
||||
return;
|
||||
return false;
|
||||
|
||||
// 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.block.ts;
|
||||
|
||||
if (!tx.isFinal(this.last.height + 1, ts))
|
||||
return;
|
||||
if (!tx.isFinal(height, ts))
|
||||
return false;
|
||||
|
||||
// Deliver me from the block size debate, please
|
||||
if (this.block.getSize() + tx.getSize() > constants.blocks.maxSize)
|
||||
return;
|
||||
return false;
|
||||
|
||||
// Add the tx to our block
|
||||
this.block.txs.push(tx);
|
||||
|
||||
// Calculate our new reward fee
|
||||
if (full)
|
||||
if (tx.hasPrevout())
|
||||
this.fee.iadd(tx.getFee());
|
||||
|
||||
// Update coinbase value
|
||||
|
||||
@ -35,7 +35,7 @@ function Node(options) {
|
||||
if (this.options.network)
|
||||
network.set(this.options.network);
|
||||
|
||||
this.block = null;
|
||||
this.blockdb = null;
|
||||
this.mempool = null;
|
||||
this.pool = null;
|
||||
this.chain = null;
|
||||
@ -50,80 +50,27 @@ inherits(Node, EventEmitter);
|
||||
Node.prototype._init = function _init() {
|
||||
var self = this;
|
||||
|
||||
this.blockdb = new bcoin.blockdb(this.options.blockdb);
|
||||
this.mempool = new bcoin.mempool(this, this.options.mempool);
|
||||
|
||||
if (!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.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) {
|
||||
self.emit('error', err);
|
||||
});
|
||||
|
||||
this.chain.on('error', function(err) {
|
||||
self.emit('error', err);
|
||||
});
|
||||
|
||||
this.pool.on('error', function(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();
|
||||
};
|
||||
|
||||
@ -140,12 +87,12 @@ Node.prototype.getCoin = function getCoin(hash, index, callback) {
|
||||
if (this.mempool.isSpent(hash, index))
|
||||
return callback(null, null);
|
||||
|
||||
this.block.getCoin(hash, index, function(err, coin) {
|
||||
this.blockdb.getCoin(hash, index, function(err, coin) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (!coin)
|
||||
return;
|
||||
return callback();
|
||||
|
||||
return callback(null, coin);
|
||||
});
|
||||
@ -159,7 +106,7 @@ Node.prototype.getCoinByAddress = function getCoinsByAddress(addresses, callback
|
||||
|
||||
mempool = this.mempool.getCoinsByAddress(addresses);
|
||||
|
||||
this.block.getCoinsByAddress(addresses, function(err, coins) {
|
||||
this.blockdb.getCoinsByAddress(addresses, function(err, coins) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
@ -181,10 +128,13 @@ Node.prototype.getTX = function getTX(hash, callback) {
|
||||
if (tx)
|
||||
return callback(null, tx);
|
||||
|
||||
this.block.getTX(hash, function(err, tx) {
|
||||
this.blockdb.getTX(hash, function(err, tx) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (!tx)
|
||||
return callback();
|
||||
|
||||
return callback(null, tx);
|
||||
});
|
||||
};
|
||||
@ -197,7 +147,7 @@ Node.prototype.isSpent = function isSpent(hash, index, callback) {
|
||||
if (this.mempool.isSpent(hash, index))
|
||||
return callback(null, true);
|
||||
|
||||
this.block.isSpent(hash, index, callback);
|
||||
this.blockdb.isSpent(hash, index, callback);
|
||||
};
|
||||
|
||||
Node.prototype.getTXByAddress = function getTXByAddress(addresses, callback) {
|
||||
@ -208,7 +158,7 @@ Node.prototype.getTXByAddress = function getTXByAddress(addresses, callback) {
|
||||
|
||||
mempool = this.mempool.getTXByAddress(addresses);
|
||||
|
||||
this.block.getTXByAddress(addresses, function(err, txs) {
|
||||
this.blockdb.getTXByAddress(addresses, function(err, txs) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
@ -218,16 +168,20 @@ Node.prototype.getTXByAddress = function getTXByAddress(addresses, callback) {
|
||||
|
||||
Node.prototype.fillCoin = function fillCoin(tx, callback) {
|
||||
callback = utils.asyncify(callback);
|
||||
|
||||
if (this.mempool.fillCoin(tx))
|
||||
return callback();
|
||||
this.block.fillCoin(tx, callback);
|
||||
|
||||
this.blockdb.fillCoin(tx, callback);
|
||||
};
|
||||
|
||||
Node.prototype.fillTX = function fillTX(tx, callback) {
|
||||
callback = utils.asyncify(callback);
|
||||
|
||||
if (this.mempool.fillTX(tx))
|
||||
return callback();
|
||||
this.block.fillTX(tx, callback);
|
||||
|
||||
this.blockdb.fillTX(tx, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -144,13 +144,11 @@ Peer.prototype._init = function init() {
|
||||
self.pool.setMisbehavior(self, 100);
|
||||
});
|
||||
|
||||
if (this.pool.options.fullNode) {
|
||||
this.once('version', function() {
|
||||
utils.debug(
|
||||
'Sent version (%s): height=%s',
|
||||
self.host, this.pool.chain.height());
|
||||
});
|
||||
}
|
||||
this.once('version', function() {
|
||||
utils.debug(
|
||||
'Sent version (%s): height=%s',
|
||||
self.host, this.pool.chain.height());
|
||||
});
|
||||
|
||||
this._ping.timer = setInterval(function() {
|
||||
self.challenge = utils.nonce();
|
||||
@ -269,7 +267,7 @@ Peer.prototype.broadcast = function broadcast(items) {
|
||||
};
|
||||
|
||||
Peer.prototype.updateWatch = function updateWatch() {
|
||||
if (this.pool.options.fullNode)
|
||||
if (!this.pool.options.spv)
|
||||
return;
|
||||
|
||||
if (this.ack)
|
||||
|
||||
@ -32,24 +32,24 @@ function Pool(options) {
|
||||
|
||||
this.options = options;
|
||||
|
||||
if (this.options.debug)
|
||||
if (options.debug)
|
||||
bcoin.debug = this.options.debug;
|
||||
|
||||
if (this.options.network)
|
||||
network.set(this.options.network);
|
||||
if (options.network)
|
||||
network.set(options.network);
|
||||
|
||||
this.options.fullNode = !!this.options.fullNode;
|
||||
options.spv = options.spv !== false;
|
||||
|
||||
if (options.type === 'spv')
|
||||
this.options.fullNode = false;
|
||||
options.spv = true;
|
||||
else if (options.type === 'full')
|
||||
this.options.fullNode = true;
|
||||
options.spv = false;
|
||||
|
||||
this.options.headers = this.options.headers;
|
||||
this.options.multiplePeers = this.options.multiplePeers;
|
||||
this.options.relay = this.options.relay == null
|
||||
? (this.options.fullNode ? true : false)
|
||||
: this.options.relay;
|
||||
options.headers = options.headers;
|
||||
options.multiplePeers = options.multiplePeers;
|
||||
options.relay = options.relay == null
|
||||
? (!options.spv ? true : false)
|
||||
: options.relay;
|
||||
|
||||
this.originalSeeds = (options.seeds || network.seeds).map(utils.parseHost);
|
||||
this.setSeeds([]);
|
||||
@ -58,20 +58,23 @@ function Pool(options) {
|
||||
this.destroyed = false;
|
||||
this.size = options.size || 32;
|
||||
|
||||
if (!this.options.fullNode) {
|
||||
if (this.options.headers == null)
|
||||
this.options.headers = true;
|
||||
if (this.options.multiplePeers == null)
|
||||
this.options.multiplePeers = true;
|
||||
this.blockdb = options.blockdb;
|
||||
this.mempool = options.mempool;
|
||||
|
||||
if (options.spv) {
|
||||
if (options.headers == null)
|
||||
options.headers = true;
|
||||
if (options.multiplePeers == null)
|
||||
options.multiplePeers = true;
|
||||
} else {
|
||||
if (this.options.headers == null)
|
||||
this.options.headers = false;
|
||||
if (this.options.multiplePeers == null)
|
||||
this.options.multiplePeers = false;
|
||||
if (options.headers == null)
|
||||
options.headers = false;
|
||||
if (options.multiplePeers == null)
|
||||
options.multiplePeers = false;
|
||||
}
|
||||
|
||||
if (!this.options.headers)
|
||||
this.options.multiplePeers = false;
|
||||
if (!options.headers)
|
||||
options.multiplePeers = false;
|
||||
|
||||
this.syncing = false;
|
||||
this.synced = false;
|
||||
@ -84,9 +87,11 @@ function Pool(options) {
|
||||
this.requestTimeout = options.requestTimeout || 600000;
|
||||
|
||||
this.chain = new bcoin.chain({
|
||||
fullNode: this.options.fullNode,
|
||||
multiplePeers: this.options.multiplePeers,
|
||||
preload: this.options.preload
|
||||
spv: options.spv,
|
||||
multiplePeers: options.multiplePeers,
|
||||
preload: options.preload,
|
||||
blockdb: options.blockdb,
|
||||
mempool: options.mempool
|
||||
});
|
||||
|
||||
this.watchMap = {};
|
||||
@ -115,7 +120,7 @@ function Pool(options) {
|
||||
this.block = {
|
||||
bestHeight: 0,
|
||||
bestHash: null,
|
||||
type: this.options.fullNode ? 'block' : 'filtered',
|
||||
type: !options.spv ? 'block' : 'filtered',
|
||||
invalid: {}
|
||||
};
|
||||
|
||||
@ -133,10 +138,6 @@ function Pool(options) {
|
||||
this.validate = {
|
||||
// 5 days scan delta for obtaining TXs
|
||||
delta: 5 * 24 * 3600,
|
||||
|
||||
// Minimum verification depth
|
||||
minDepth: options.minValidateDepth || 0,
|
||||
|
||||
// getTX map
|
||||
map: {}
|
||||
};
|
||||
@ -148,7 +149,7 @@ function Pool(options) {
|
||||
};
|
||||
|
||||
// Added and watched wallets
|
||||
this.options.wallets = this.options.wallets || [];
|
||||
options.wallets = options.wallets || [];
|
||||
this.wallets = [];
|
||||
|
||||
Pool.global = this;
|
||||
@ -615,7 +616,7 @@ Pool.prototype._handleBlock = function _handleBlock(block, peer, callback) {
|
||||
requested = self._response(block);
|
||||
|
||||
// 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) {
|
||||
if (self.isWatched(tx))
|
||||
self.emit('watched', tx, peer);
|
||||
@ -857,22 +858,35 @@ Pool.prototype._handleTX = function _handleTX(tx, peer, callback) {
|
||||
|
||||
callback = utils.asyncify(callback);
|
||||
|
||||
this._prehandleTX(tx, peer, function(err) {
|
||||
var requested, added;
|
||||
function addMempool(tx, peer, callback) {
|
||||
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)
|
||||
return callback(err);
|
||||
|
||||
requested = self._response(tx);
|
||||
added = self._addTX(tx, 1);
|
||||
addMempool(tx, peer, function(err) {
|
||||
var requested, added;
|
||||
|
||||
if (added || tx.block)
|
||||
self.emit('tx', tx, peer);
|
||||
if (err)
|
||||
utils.debug('Mempool error: %s', err.message);
|
||||
|
||||
if (!self.options.fullNode && tx.block)
|
||||
self.emit('watched', tx, peer);
|
||||
requested = self._response(tx);
|
||||
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);
|
||||
});
|
||||
|
||||
if (self.options.fullNode)
|
||||
if (!self.options.spv)
|
||||
return;
|
||||
|
||||
if (self._pendingSearch)
|
||||
@ -1323,7 +1337,7 @@ Pool.prototype.searchWallet = function(w, h) {
|
||||
|
||||
assert(!this.loading);
|
||||
|
||||
if (this.options.fullNode)
|
||||
if (!this.options.spv)
|
||||
return;
|
||||
|
||||
if (w == null) {
|
||||
@ -1393,7 +1407,7 @@ Pool.prototype.search = function search(id, range, e) {
|
||||
|
||||
assert(!this.loading);
|
||||
|
||||
if (this.options.fullNode)
|
||||
if (!this.options.spv)
|
||||
return;
|
||||
|
||||
if (typeof e === 'function') {
|
||||
@ -1570,7 +1584,7 @@ Pool.prototype.getTX = function getTX(hash, range, cb) {
|
||||
if (!this.peers.load)
|
||||
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'));
|
||||
|
||||
hash = utils.toHex(hash);
|
||||
|
||||
@ -272,3 +272,33 @@ exports.userAgent = '/bcoin:' + exports.userVersion + '/';
|
||||
|
||||
exports.banTime = 24 * 60 * 60;
|
||||
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;
|
||||
|
||||
@ -198,10 +198,10 @@ script.verify = function verify(input, output, tx, i, flags) {
|
||||
var copy, res, redeem;
|
||||
var stack = [];
|
||||
|
||||
if (!flags)
|
||||
flags = {};
|
||||
if (flags == null)
|
||||
flags = constants.flags.STANDARD_VERIFY_FLAGS;
|
||||
|
||||
if (flags.sigpushonly !== false) {
|
||||
if (flags & constants.flags.VERIFY_SIGPUSHONLY) {
|
||||
if (!script.isPushOnly(input))
|
||||
return false;
|
||||
}
|
||||
@ -210,7 +210,7 @@ script.verify = function verify(input, output, tx, i, flags) {
|
||||
script.execute(input, stack, tx, i, flags);
|
||||
|
||||
// Copy the stack for P2SH
|
||||
if (flags.verifyp2sh !== false)
|
||||
if (flags & constants.flags.VERIFY_P2SH)
|
||||
copy = stack.slice();
|
||||
|
||||
// Execute the previous output script
|
||||
@ -221,7 +221,7 @@ script.verify = function verify(input, output, tx, i, flags) {
|
||||
return false;
|
||||
|
||||
// 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
|
||||
if (!script.isPushOnly(input))
|
||||
return false;
|
||||
@ -250,7 +250,7 @@ script.verify = function verify(input, output, tx, i, flags) {
|
||||
}
|
||||
|
||||
// Ensure there is nothing left on the stack
|
||||
if (flags.cleanstack !== false) {
|
||||
if (flags & constants.flags.VERIFY_CLEANSTACK) {
|
||||
if (stack.length !== 0)
|
||||
return false;
|
||||
}
|
||||
@ -370,8 +370,8 @@ script._next = function _next(to, s, pc) {
|
||||
script.execute = function execute(data, stack, tx, index, flags, recurse) {
|
||||
var s = data.slice();
|
||||
|
||||
if (!flags)
|
||||
flags = {};
|
||||
if (flags == null)
|
||||
flags = constants.flags.STANDARD_VERIFY_FLAGS;
|
||||
|
||||
if (s.length > constants.script.maxOps)
|
||||
return false;
|
||||
@ -418,7 +418,7 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
|
||||
case 'nop8':
|
||||
case 'nop9':
|
||||
case 'nop10': {
|
||||
if (flags.discourage_nops !== false)
|
||||
if (flags & constants.flags.VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
@ -908,7 +908,7 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
|
||||
|
||||
val = stack.pop();
|
||||
|
||||
if (flags.verifynulldummy !== false) {
|
||||
if (flags & constants.flags.VERIFY_NULLDUMMY) {
|
||||
if (!script.isDummy(val))
|
||||
return false;
|
||||
}
|
||||
@ -925,8 +925,8 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
|
||||
}
|
||||
case 'checklocktimeverify': {
|
||||
// OP_CHECKLOCKTIMEVERIFY = OP_NOP2
|
||||
if (flags.checklocktimeverify === false) {
|
||||
if (flags.discourage_nops !== false)
|
||||
if (!(flags & constants.flags.VERIFY_CHECKLOCKTIMEVERIFY)) {
|
||||
if (flags & constants.flags.VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
@ -965,8 +965,8 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
|
||||
}
|
||||
case 'nop1': {
|
||||
// OP_EVAL = OP_NOP1
|
||||
if (flags['eval'] !== true) {
|
||||
if (flags.discourage_nops !== false)
|
||||
if (!script.allowEval) {
|
||||
if (flags & constants.flags.VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
@ -1126,10 +1126,13 @@ script.removeData = function removeData(s, data) {
|
||||
};
|
||||
|
||||
script.checkPush = function checkPush(op, value, flags) {
|
||||
if (!flags)
|
||||
flags = {};
|
||||
if (flags == null)
|
||||
flags = constants.flags.STANDARD_VERIFY_FLAGS;
|
||||
|
||||
if (flags.minimaldata === false)
|
||||
// Disabled for now.
|
||||
return true;
|
||||
|
||||
if (!(flags & constants.flags.VERIFY_MINIMALDATA))
|
||||
return true;
|
||||
|
||||
if (!op.pushdata)
|
||||
@ -2038,13 +2041,13 @@ script.isData = function isData(data) {
|
||||
};
|
||||
|
||||
script.isValidKey = function isValidKey(key, flags) {
|
||||
if (!flags)
|
||||
flags = {};
|
||||
if (flags == null)
|
||||
flags = constants.flags.STANDARD_VERIFY_FLAGS;
|
||||
|
||||
if (!utils.isBuffer(key))
|
||||
return false;
|
||||
|
||||
if (flags.strictenc !== false) {
|
||||
if (flags & constants.flags.VERIFY_STRICTENC) {
|
||||
if (!script.isKeyEncoding(key))
|
||||
return false;
|
||||
}
|
||||
@ -2073,8 +2076,8 @@ script.isKeyEncoding = function isKeyEncoding(key) {
|
||||
};
|
||||
|
||||
script.isValidSignature = function isValidSignature(sig, flags) {
|
||||
if (!flags)
|
||||
flags = {};
|
||||
if (flags == null)
|
||||
flags = constants.flags.STANDARD_VERIFY_FLAGS;
|
||||
|
||||
if (!utils.isBuffer(sig))
|
||||
return false;
|
||||
@ -2083,19 +2086,19 @@ script.isValidSignature = function isValidSignature(sig, flags) {
|
||||
if (sig.length === 0)
|
||||
return true;
|
||||
|
||||
if (flags.dersig !== false
|
||||
|| flags.low_s !== false
|
||||
|| flags.strictenc !== false) {
|
||||
if ((flags & constants.flags.VERIFY_DERSIG)
|
||||
|| (flags & constants.flags.VERIFY_LOW_S)
|
||||
|| (flags & constants.flags.VERIFY_STRICTENC)) {
|
||||
if (!script.isSignatureEncoding(sig))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flags.low_s !== false) {
|
||||
if (flags & constants.flags.VERIFY_LOW_S) {
|
||||
if (!script.isLowDER(sig))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flags.strictenc !== false) {
|
||||
if (flags & constants.flags.VERIFY_STRICTENC) {
|
||||
if (!script.isHashType(sig))
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -809,13 +809,6 @@ TX.prototype.signatureHash = function signatureHash(index, s, type) {
|
||||
if (typeof index !== 'number')
|
||||
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')
|
||||
type = constants.hashType[type];
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user