standardness. chain. pool.
This commit is contained in:
parent
83c5a6f906
commit
050d801849
@ -48,6 +48,7 @@ function Block(data, subtype) {
|
|||||||
|
|
||||||
this.valid = null;
|
this.valid = null;
|
||||||
this._hash = null;
|
this._hash = null;
|
||||||
|
this._cbHeight = null;
|
||||||
|
|
||||||
// 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
|
||||||
@ -317,6 +318,35 @@ Block.prototype.getHeight = function getHeight() {
|
|||||||
return this.chain.getHeight(this.hash('hex'));
|
return this.chain.getHeight(this.hash('hex'));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Block.prototype.getCoinbaseHeight = function getCoinbaseHeight() {
|
||||||
|
var coinbase, s, height;
|
||||||
|
|
||||||
|
if (this.subtype !== 'block')
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (this.version < 2)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (this._cbHeight != null)
|
||||||
|
return this._cbHeight;
|
||||||
|
|
||||||
|
coinbase = this.txs[0];
|
||||||
|
|
||||||
|
if (!coinbase || coinbase.inputs.length === 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
s = coinbase.inputs[0].script;
|
||||||
|
|
||||||
|
if (Array.isArray(s[0]))
|
||||||
|
height = bcoin.script.num(s[0], true);
|
||||||
|
else
|
||||||
|
height = -1;
|
||||||
|
|
||||||
|
this._cbHeight = height;
|
||||||
|
|
||||||
|
return height;
|
||||||
|
};
|
||||||
|
|
||||||
Block.prototype.getNextBlock = function getNextBlock() {
|
Block.prototype.getNextBlock = function getNextBlock() {
|
||||||
var next;
|
var next;
|
||||||
|
|
||||||
|
|||||||
@ -50,6 +50,7 @@ function Chain(options) {
|
|||||||
this.orphanLimit = options.orphanLimit || 20 * 1024 * 1024;
|
this.orphanLimit = options.orphanLimit || 20 * 1024 * 1024;
|
||||||
this.pendingLimit = options.pendingLimit || 20 * 1024 * 1024;
|
this.pendingLimit = options.pendingLimit || 20 * 1024 * 1024;
|
||||||
this.invalid = {};
|
this.invalid = {};
|
||||||
|
this.bestHeight = -1;
|
||||||
|
|
||||||
this.orphan = {
|
this.orphan = {
|
||||||
map: {},
|
map: {},
|
||||||
@ -153,11 +154,6 @@ Chain.prototype._init = function _init() {
|
|||||||
|
|
||||||
utils.debug('Starting chain load at height: %s', i);
|
utils.debug('Starting chain load at height: %s', i);
|
||||||
|
|
||||||
function doneForReal() {
|
|
||||||
self.loading = false;
|
|
||||||
self.emit('load');
|
|
||||||
}
|
|
||||||
|
|
||||||
function done(height) {
|
function done(height) {
|
||||||
if (height != null) {
|
if (height != null) {
|
||||||
utils.debug(
|
utils.debug(
|
||||||
@ -168,31 +164,11 @@ Chain.prototype._init = function _init() {
|
|||||||
utils.debug('Chain successfully loaded.');
|
utils.debug('Chain successfully loaded.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!self.blockdb)
|
self.syncHeight(function(err) {
|
||||||
return doneForReal();
|
|
||||||
|
|
||||||
self.blockdb.getHeight(function(err, height) {
|
|
||||||
if (err)
|
if (err)
|
||||||
throw err;
|
throw err;
|
||||||
|
self.loading = false;
|
||||||
assert(height !== -1);
|
self.emit('load');
|
||||||
|
|
||||||
if (height === self.tip.height)
|
|
||||||
return doneForReal();
|
|
||||||
|
|
||||||
utils.debug('ChainDB and BlockDB are out of sync. Syncing...');
|
|
||||||
|
|
||||||
if (height < self.tip.height)
|
|
||||||
return self.resetHeight(height);
|
|
||||||
|
|
||||||
if (height > self.tip.height) {
|
|
||||||
return self.blockdb.resetHeight(self.tip.height, function(err) {
|
|
||||||
if (err)
|
|
||||||
throw err;
|
|
||||||
|
|
||||||
return doneForReal();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -534,16 +510,7 @@ Chain.prototype._verify = function _verify(block, prev) {
|
|||||||
|
|
||||||
// Make sure the height contained in the coinbase is correct.
|
// Make sure the height contained in the coinbase is correct.
|
||||||
if (coinbaseHeight) {
|
if (coinbaseHeight) {
|
||||||
cb = bcoin.script.getCoinbaseData(block.txs[0].inputs[0].script);
|
if (block.getCoinbaseHeight() !== height) {
|
||||||
|
|
||||||
// Make sure the coinbase is parseable.
|
|
||||||
if (!cb) {
|
|
||||||
utils.debug('Block has malformed coinbase: %s', block.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', block.rhash);
|
utils.debug('Block has bad coinbase height: %s', block.rhash);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -791,15 +758,11 @@ Chain.prototype.resetHeight = function resetHeight(height) {
|
|||||||
var count = this.db.count();
|
var count = this.db.count();
|
||||||
var i, existing;
|
var i, existing;
|
||||||
|
|
||||||
assert(height < count);
|
assert(height <= count - 1);
|
||||||
|
assert(this.tip);
|
||||||
|
|
||||||
// Reset the orphan map completely. There may
|
if (height === count - 1)
|
||||||
// have been some orphans on a forked chain we
|
return;
|
||||||
// no longer need.
|
|
||||||
this.orphan.map = {};
|
|
||||||
this.orphan.bmap = {};
|
|
||||||
this.orphan.count = 0;
|
|
||||||
this.orphan.size = 0;
|
|
||||||
|
|
||||||
for (i = height + 1; i < count; i++) {
|
for (i = height + 1; i < count; i++) {
|
||||||
existing = this.db.get(i);
|
existing = this.db.get(i);
|
||||||
@ -808,12 +771,130 @@ Chain.prototype.resetHeight = function resetHeight(height) {
|
|||||||
this.db.remove(i);
|
this.db.remove(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset the orphan map completely. There may
|
||||||
|
// have been some orphans on a forked chain we
|
||||||
|
// no longer need.
|
||||||
|
this.emit('purge', this.orphan.count, this.orphan.size);
|
||||||
|
this.orphan.map = {};
|
||||||
|
this.orphan.bmap = {};
|
||||||
|
this.orphan.count = 0;
|
||||||
|
this.orphan.size = 0;
|
||||||
|
|
||||||
this.tip = this.db.get(height);
|
this.tip = this.db.get(height);
|
||||||
assert(this.tip);
|
assert(this.tip);
|
||||||
this.height = this.tip.height;
|
this.height = this.tip.height;
|
||||||
this.emit('tip', this.tip);
|
this.emit('tip', this.tip);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Chain.prototype.revertHeight = function revertHeight(height, callback) {
|
||||||
|
var self = this;
|
||||||
|
var chainHeight;
|
||||||
|
var lock = this.locked;
|
||||||
|
|
||||||
|
assert(!this.locked);
|
||||||
|
|
||||||
|
callback = utils.asyncify(callback);
|
||||||
|
|
||||||
|
this.locked = true;
|
||||||
|
|
||||||
|
function done(err, result) {
|
||||||
|
self.locked = lock;
|
||||||
|
callback(err, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
chainHeight = this.db.count() - 1;
|
||||||
|
|
||||||
|
if (chainHeight < 0)
|
||||||
|
return done(new Error('Bad chain height.'));
|
||||||
|
|
||||||
|
if (!this.blockdb) {
|
||||||
|
if (height > chainHeight)
|
||||||
|
return done(new Error('Cannot reset height.'));
|
||||||
|
this.resetHeight(height);
|
||||||
|
return done();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.blockdb.getHeight(function(err, blockHeight) {
|
||||||
|
if (err)
|
||||||
|
return done(err);
|
||||||
|
|
||||||
|
if (blockHeight < 0)
|
||||||
|
return done(new Error('Bad block height.'));
|
||||||
|
|
||||||
|
if (chainHeight !== blockHeight)
|
||||||
|
return done(new Error('ChainDB and BlockDB are out of sync.'));
|
||||||
|
|
||||||
|
if (height === chainHeight)
|
||||||
|
return done();
|
||||||
|
|
||||||
|
if (height > chainHeight)
|
||||||
|
return done(new Error('Cannot reset height.'));
|
||||||
|
|
||||||
|
self.blockdb.resetHeight(height, function(err) {
|
||||||
|
if (err)
|
||||||
|
return done(err);
|
||||||
|
|
||||||
|
self.resetHeight(height);
|
||||||
|
|
||||||
|
return done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Chain.prototype.syncHeight = function syncHeight(callback) {
|
||||||
|
var self = this;
|
||||||
|
var chainHeight;
|
||||||
|
var lock = this.locked;
|
||||||
|
|
||||||
|
callback = utils.asyncify(callback);
|
||||||
|
|
||||||
|
assert(!this.locked);
|
||||||
|
|
||||||
|
this.locked = true;
|
||||||
|
|
||||||
|
function done(err, result) {
|
||||||
|
self.locked = lock;
|
||||||
|
callback(err, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
chainHeight = this.db.count() - 1;
|
||||||
|
|
||||||
|
if (chainHeight < 0)
|
||||||
|
return done(new Error('Bad chain height.'));
|
||||||
|
|
||||||
|
if (!this.blockdb)
|
||||||
|
return done();
|
||||||
|
|
||||||
|
this.blockdb.getHeight(function(err, blockHeight) {
|
||||||
|
if (err)
|
||||||
|
return done(err);
|
||||||
|
|
||||||
|
if (blockHeight < 0)
|
||||||
|
return done(new Error('Bad block height.'));
|
||||||
|
|
||||||
|
if (blockHeight === chainHeight)
|
||||||
|
return done();
|
||||||
|
|
||||||
|
utils.debug('ChainDB and BlockDB are out of sync.');
|
||||||
|
|
||||||
|
if (blockHeight < chainHeight) {
|
||||||
|
utils.debug('BlockDB is higher than ChainDB. Syncing...');
|
||||||
|
self.resetHeight(blockHeight);
|
||||||
|
return done();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blockHeight > chainHeight) {
|
||||||
|
utils.debug('ChainDB is higher than BlockDB. Syncing...');
|
||||||
|
self.blockdb.resetHeight(chainHeight, function(err) {
|
||||||
|
if (err)
|
||||||
|
return done(err);
|
||||||
|
|
||||||
|
return done();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
Chain.prototype.resetTime = function resetTime(ts) {
|
Chain.prototype.resetTime = function resetTime(ts) {
|
||||||
var entry = this.byTime(ts);
|
var entry = this.byTime(ts);
|
||||||
if (!entry)
|
if (!entry)
|
||||||
@ -826,6 +907,8 @@ Chain.prototype.add = function add(initial, peer, callback) {
|
|||||||
var host = peer ? peer.host : 'unknown';
|
var host = peer ? peer.host : 'unknown';
|
||||||
var total = 0;
|
var total = 0;
|
||||||
|
|
||||||
|
assert(!this.loading);
|
||||||
|
|
||||||
if (this.locked) {
|
if (this.locked) {
|
||||||
this.pending.push([initial, peer, callback]);
|
this.pending.push([initial, peer, callback]);
|
||||||
this.pendingBlocks[initial.hash('hex')] = true;
|
this.pendingBlocks[initial.hash('hex')] = true;
|
||||||
@ -905,6 +988,13 @@ Chain.prototype.add = function add(initial, peer, callback) {
|
|||||||
return done();
|
return done();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the best height based on the coinbase.
|
||||||
|
// We do this even for orphans (peers will send
|
||||||
|
// us their highest block during the initial
|
||||||
|
// getblocks sync, making it an orphan).
|
||||||
|
if (block.getCoinbaseHeight() > self.bestHeight)
|
||||||
|
self.bestHeight = block.getCoinbaseHeight();
|
||||||
|
|
||||||
// If previous block wasn't ever seen,
|
// If previous block wasn't ever seen,
|
||||||
// add it current to orphans and break.
|
// add it current to orphans and break.
|
||||||
if (prevHeight == null) {
|
if (prevHeight == null) {
|
||||||
|
|||||||
@ -229,7 +229,7 @@ Mempool.prototype.addTX = function addTX(tx, peer, callback) {
|
|||||||
return callback(new Error('Previous outputs not found.'));
|
return callback(new Error('Previous outputs not found.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tx.isStandard()) {
|
if (!tx.isStandard(flags)) {
|
||||||
return callback(new Error('TX is not standard.'));
|
return callback(new Error('TX is not standard.'));
|
||||||
peer.reject({
|
peer.reject({
|
||||||
data: tx.hash(),
|
data: tx.hash(),
|
||||||
@ -239,7 +239,7 @@ Mempool.prototype.addTX = function addTX(tx, peer, callback) {
|
|||||||
return callback(new Error('TX is not standard.'));
|
return callback(new Error('TX is not standard.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tx.isStandardInputs()) {
|
if (!tx.isStandardInputs(flags)) {
|
||||||
return callback(new Error('TX inputs are not standard.'));
|
return callback(new Error('TX inputs are not standard.'));
|
||||||
peer.reject({
|
peer.reject({
|
||||||
data: tx.hash(),
|
data: tx.hash(),
|
||||||
|
|||||||
@ -50,8 +50,6 @@ function Peer(pool, options) {
|
|||||||
this.lastPong = 0;
|
this.lastPong = 0;
|
||||||
|
|
||||||
this.banScore = 0;
|
this.banScore = 0;
|
||||||
this.orphans = 0;
|
|
||||||
this.orphanTime = 0;
|
|
||||||
|
|
||||||
if (options.socket) {
|
if (options.socket) {
|
||||||
this.socket = options.socket;
|
this.socket = options.socket;
|
||||||
|
|||||||
@ -118,7 +118,7 @@ function Pool(options) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.block = {
|
this.block = {
|
||||||
bestHeight: 0,
|
versionHeight: 0,
|
||||||
bestHash: null,
|
bestHash: null,
|
||||||
type: !options.spv ? 'block' : 'filtered'
|
type: !options.spv ? 'block' : 'filtered'
|
||||||
};
|
};
|
||||||
@ -222,23 +222,15 @@ Pool.prototype._init = function _init() {
|
|||||||
if (!peer)
|
if (!peer)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Resolve orphan chain
|
// Increase banscore by 10 if we're using getheaders.
|
||||||
if (!self.options.headers) {
|
if (self.options.headers) {
|
||||||
// Make sure the peer doesn't send us
|
|
||||||
// more than 200 orphans every 3 minutes.
|
|
||||||
if (self.isOrphaning(peer)) {
|
|
||||||
utils.debug('Peer is orphaning (%s)', host);
|
|
||||||
self.setMisbehavior(peer, 100);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve orphan chain.
|
|
||||||
self.loadOrphan(self.peers.load, null, data.hash);
|
|
||||||
} else {
|
|
||||||
// Increase banscore by 10 if we're using getheaders.
|
|
||||||
if (!self.options.multiplePeers)
|
if (!self.options.multiplePeers)
|
||||||
self.setMisbehavior(peer, 10);
|
self.setMisbehavior(peer, 10);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resolve orphan chain
|
||||||
|
self.loadOrphan(self.peers.load, null, data.hash);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.options.wallets.forEach(function(w) {
|
this.options.wallets.forEach(function(w) {
|
||||||
@ -582,30 +574,34 @@ Pool.prototype._handleBlocks = function _handleBlocks(hashes, peer) {
|
|||||||
for (i = 0; i < hashes.length; i++) {
|
for (i = 0; i < hashes.length; i++) {
|
||||||
hash = hashes[i];
|
hash = hashes[i];
|
||||||
|
|
||||||
|
// Resolve orphan chain.
|
||||||
if (self.chain.hasOrphan(hash)) {
|
if (self.chain.hasOrphan(hash)) {
|
||||||
utils.debug('Peer sent a hash that is already a known orphan.');
|
utils.debug('Peer sent a hash that is already a known orphan.');
|
||||||
|
|
||||||
// Make sure the peer doesn't send us
|
|
||||||
// more than 200 orphans every 3 minutes.
|
|
||||||
if (self.isOrphaning(peer)) {
|
|
||||||
utils.debug('Peer is orphaning (%s)', peer.host);
|
|
||||||
self.setMisbehavior(peer, 100);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve orphan chain.
|
|
||||||
self.loadOrphan(peer, null, hash);
|
self.loadOrphan(peer, null, hash);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request block if we don't have it or if
|
// Request a block if we don't have it.
|
||||||
// this is the last hash: this is done as
|
if (!self.chain.has(hash)) {
|
||||||
// a failsafe because we _need_ to request
|
|
||||||
// the hashContinue no matter what.
|
|
||||||
if (!self.chain.has(hash) || i === hashes.length - 1) {
|
|
||||||
self._request(peer, self.block.type, hash);
|
self._request(peer, self.block.type, hash);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Normally we request the hashContinue.
|
||||||
|
// In the odd case where we already have
|
||||||
|
// it, we can do one of two things: either
|
||||||
|
// force re-downloading of the block to
|
||||||
|
// continue the sync, or do a getblocks
|
||||||
|
// from the last hash.
|
||||||
|
if (i === hashes.length - 1) {
|
||||||
|
// Request more hashes:
|
||||||
|
self.loadBlocks(peer, hash, null);
|
||||||
|
|
||||||
|
// Re-download the block (traditional method):
|
||||||
|
// self._request(peer, self.block.type, hash);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -682,9 +678,10 @@ Pool.prototype._handleBlock = function _handleBlock(block, peer, callback) {
|
|||||||
|
|
||||||
if (self.chain.height % 20 === 0) {
|
if (self.chain.height % 20 === 0) {
|
||||||
utils.debug(
|
utils.debug(
|
||||||
'Got: %s from %s chain len %d blocks %d orp %d act %d queue %d target %s peers %d pending %d',
|
'Status: %s @ %s height=%d blocks=%d orphan=%d active=%d'
|
||||||
|
+ ' queue=%d target=%s peers=%d pending=%d highest=%d',
|
||||||
block.rhash,
|
block.rhash,
|
||||||
new Date(block.ts * 1000).toString(),
|
new Date(block.ts * 1000).toISOString(),
|
||||||
self.chain.height,
|
self.chain.height,
|
||||||
self.chain.total,
|
self.chain.total,
|
||||||
self.chain.orphan.count,
|
self.chain.orphan.count,
|
||||||
@ -692,7 +689,8 @@ Pool.prototype._handleBlock = function _handleBlock(block, peer, callback) {
|
|||||||
peer._blockQueue.length,
|
peer._blockQueue.length,
|
||||||
self.chain.currentTarget(),
|
self.chain.currentTarget(),
|
||||||
self.peers.all.length,
|
self.peers.all.length,
|
||||||
self.chain.pending.length);
|
self.chain.pending.length,
|
||||||
|
self.chain.bestHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
return callback(null, true);
|
return callback(null, true);
|
||||||
@ -793,8 +791,8 @@ Pool.prototype._createPeer = function _createPeer(options) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
peer.on('version', function(version) {
|
peer.on('version', function(version) {
|
||||||
if (version.height > self.block.bestHeight)
|
if (version.height > self.block.versionHeight)
|
||||||
self.block.bestHeight = version.height;
|
self.block.versionHeight = version.height;
|
||||||
self.emit('version', version, peer);
|
self.emit('version', version, peer);
|
||||||
utils.debug(
|
utils.debug(
|
||||||
'Received version from %s: version=%d height=%d agent=%s',
|
'Received version from %s: version=%d height=%d agent=%s',
|
||||||
@ -1821,23 +1819,6 @@ Pool.prototype.removeSeed = function removeSeed(seed) {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
Pool.prototype.isOrphaning = function isOrphaning(peer) {
|
|
||||||
if (!this.options.orphanDOS)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (utils.now() > peer.orphanTime + 3 * 60) {
|
|
||||||
peer.orphans = 0;
|
|
||||||
peer.orphanTime = utils.now();
|
|
||||||
}
|
|
||||||
|
|
||||||
peer.orphans += 1;
|
|
||||||
|
|
||||||
if (peer.orphans > 200)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
Pool.prototype.setMisbehavior = function setMisbehavior(peer, dos) {
|
Pool.prototype.setMisbehavior = function setMisbehavior(peer, dos) {
|
||||||
peer.banScore += dos;
|
peer.banScore += dos;
|
||||||
|
|
||||||
|
|||||||
@ -204,6 +204,7 @@ exports.block = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
exports.tx = {
|
exports.tx = {
|
||||||
|
version: 1,
|
||||||
maxSize: 100000,
|
maxSize: 100000,
|
||||||
minFee: 10000,
|
minFee: 10000,
|
||||||
bareMultisig: true,
|
bareMultisig: true,
|
||||||
|
|||||||
@ -1303,9 +1303,10 @@ script.getOutputType = function getOutputType(s) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
script.isStandard = function isStandard(s) {
|
script.isStandard = function isStandard(s) {
|
||||||
|
var type = script.getType(s);
|
||||||
var m, n;
|
var m, n;
|
||||||
|
|
||||||
if (script.isMultisig(s)) {
|
if (type === 'multisig') {
|
||||||
m = s[0];
|
m = s[0];
|
||||||
n = s[s.length - 2];
|
n = s[s.length - 2];
|
||||||
|
|
||||||
@ -1314,12 +1315,12 @@ script.isStandard = function isStandard(s) {
|
|||||||
|
|
||||||
if (m < 1 || m > n)
|
if (m < 1 || m > n)
|
||||||
return false;
|
return false;
|
||||||
} else if (script.isNulldata(s)) {
|
} else if (type === 'nulldata') {
|
||||||
if (script.getSize(s) > constants.script.maxOpReturnBytes)
|
if (script.getSize(s) > constants.script.maxOpReturnBytes)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return type != null;
|
return type !== 'unknown';
|
||||||
};
|
};
|
||||||
|
|
||||||
script.getSize = function getSize(s) {
|
script.getSize = function getSize(s) {
|
||||||
|
|||||||
@ -1536,10 +1536,13 @@ TX.prototype.getSigops = function getSigops(scriptHash, accurate) {
|
|||||||
return n;
|
return n;
|
||||||
};
|
};
|
||||||
|
|
||||||
TX.prototype.isStandard = function isStandard() {
|
TX.prototype.isStandard = function isStandard(flags) {
|
||||||
var i, input, output, type;
|
var i, input, output, type;
|
||||||
var nulldata = 0;
|
var nulldata = 0;
|
||||||
|
|
||||||
|
if (flags == null)
|
||||||
|
flags = constants.flags.STANDARD_VERIFY_FLAGS;
|
||||||
|
|
||||||
if (this.version > constants.tx.version || this.version < 1)
|
if (this.version > constants.tx.version || this.version < 1)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -1556,8 +1559,10 @@ TX.prototype.isStandard = function isStandard() {
|
|||||||
if (this.isCoinbase())
|
if (this.isCoinbase())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!bcoin.script.isPushOnly(input.script))
|
if (flags & constants.flags.VERIFY_SIGPUSHONLY) {
|
||||||
return false;
|
if (!bcoin.script.isPushOnly(input.script))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < this.outputs.length; i++) {
|
for (i = 0; i < this.outputs.length; i++) {
|
||||||
@ -1567,7 +1572,7 @@ TX.prototype.isStandard = function isStandard() {
|
|||||||
if (!bcoin.script.isStandard(output.script))
|
if (!bcoin.script.isStandard(output.script))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!type)
|
if (type === 'unknown')
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (type === 'nulldata') {
|
if (type === 'nulldata') {
|
||||||
@ -1589,7 +1594,11 @@ TX.prototype.isStandard = function isStandard() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
TX.prototype.isStandardInputs = function isStandardInputs(flags) {
|
TX.prototype.isStandardInputs = function isStandardInputs(flags) {
|
||||||
var i, input, args, stack, res, s, targs;
|
var i, input, args, stack, res, redeem, targs;
|
||||||
|
var maxSigops = constants.script.maxScripthashSigops;
|
||||||
|
|
||||||
|
if (flags == null)
|
||||||
|
flags = constants.flags.STANDARD_VERIFY_FLAGS;
|
||||||
|
|
||||||
if (this.isCoinbase())
|
if (this.isCoinbase())
|
||||||
return true;
|
return true;
|
||||||
@ -1612,25 +1621,31 @@ TX.prototype.isStandardInputs = function isStandardInputs(flags) {
|
|||||||
if (!res)
|
if (!res)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (bcoin.script.isScripthash(input.output.script)) {
|
if ((flags & constants.flags.VERIFY_P2SH)
|
||||||
|
&& bcoin.script.isScripthash(input.output.script)) {
|
||||||
if (stack.length === 0)
|
if (stack.length === 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
s = stack[stack.length - 1];
|
redeem = bcoin.script.getRedeem(stack);
|
||||||
|
|
||||||
if (!Array.isArray(s))
|
if (!redeem)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
s = bcoin.script.decode(s);
|
// Not accurate?
|
||||||
|
if (bcoin.script.getSize(redeem) > 520)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (bcoin.script.getType(s) !== 'unknown') {
|
// Also consider scripthash "unknown"?
|
||||||
targs = bcoin.script.getArgs(s);
|
if (bcoin.script.getType(redeem) === 'unknown') {
|
||||||
if (targs < 0)
|
if (bcoin.script.getSigops(redeem, true) > maxSigops)
|
||||||
return false;
|
return false;
|
||||||
args += targs;
|
continue;
|
||||||
} else {
|
|
||||||
return script.getSigops(s, true) <= constants.script.maxScripthashSigops;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
targs = bcoin.script.getArgs(redeem);
|
||||||
|
if (targs < 0)
|
||||||
|
return false;
|
||||||
|
args += targs;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stack.length !== args)
|
if (stack.length !== args)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user