more block verification.

This commit is contained in:
Christopher Jeffrey 2016-01-06 13:08:50 -08:00
parent e1ec89fe7d
commit 8d120a4fd6
11 changed files with 372 additions and 71 deletions

View File

@ -271,79 +271,171 @@ Block.prototype._verify = function _verify() {
}; };
Block.prototype.postVerify = function postVerify() { Block.prototype.postVerify = function postVerify() {
var prev, i, tx, cb, sigops;
var flags = {}; var flags = {};
var bip16time = 1333238400; var prev, height, i, j, tx, cb, sigops, input;
var strictp2sh = this.ts >= bip16time;
if (this.subtype !== 'block') if (this.subtype !== 'block')
return true; return true;
if (this.isGenesis())
return true;
if (!this.chain) if (!this.chain)
return true; return true;
prev = this.chain.getBlock(this.prevBlock); prev = this.chain.getBlock(this.prevBlock);
height = prev.height + 1;
// Ensure it's not an orphan // Ensure it's not an orphan
if (!prev) if (!prev) {
console.log('NO PREV: %s', height);
return false; return false;
}
// Ensure the timestamp is correct // Ensure the timestamp is correct
if (this.ts <= prev.getMedianTime()) if (this.ts <= prev.getMedianTime()) {
console.log('BAD TIME: %s', height);
return false; return false;
}
// Test all txs // Test all txs
for (i = 0; i < this.txs.length; i++) { for (i = 0; i < this.txs.length; i++) {
tx = this.txs[i]; tx = this.txs[i];
// TXs must be finalized with regards to seq and locktime // TXs must be finalized with regards to seq and locktime
if (!tx.isFinal(this, prev)) if (!tx.isFinal(this, prev)) {
console.log('IS NOT FINAL: %s', height);
return false; return false;
}
} }
if (this.bits !== this.chain.target(prev, this)) // Ensure the miner's target is equal to what we expect
if (this.bits !== this.chain.target(prev, this)) {
console.log('BAD TARGET: %s', height);
return false; return false;
}
if (this.version < 2 && prev.isOutdated(2)) // Only allow version 2 blocks (coinbase height)
// once the majority of blocks are using it.
if (this.version < 2 && prev.isOutdated(2)) {
console.log('OUTDATED 2: %s', height);
return false; return false;
}
if (this.version < 3 && prev.isOutdated(3)) // Only allow version 3 blocks (sig validation)
// once the majority of blocks are using it.
if (this.version < 3 && prev.isOutdated(3)) {
console.log('OUTDATED 3: %s', height);
return false; return false;
}
if (this.version < 4 && prev.isOutdated(4)) // Only allow version 4 blocks (checklocktimeverify)
// once the majority of blocks are using it.
if (this.version < 4 && prev.isOutdated(4)) {
console.log('OUTDATED 4: %s', height);
return false; return false;
}
// Enforce height in coinbase // Make sure the height contained in the
if (this.version >= 2 && prev.needsUpgrade(2)) { // coinbase is correct.
if (this.version >= 2 && prev.isUpgraded(2)) {
cb = bcoin.script.isCoinbase(this.txs[0].inputs[0].script, this); cb = bcoin.script.isCoinbase(this.txs[0].inputs[0].script, this);
if (!cb) if (!cb) {
console.log('BAD COINBASE: %s', height);
return false; return false;
}
if (cb.height !== prev.height + 1) if (cb.height !== height) {
console.log('BAD COINBASE HEIGHT: %s', height);
return false; return false;
}
} }
// sig validation (bip66) // Signature validation is now enforced (bip66)
if (this.version >= 3 && prev.needsUpgrade(3)) if (this.version >= 3 && prev.isUpgraded(3))
flags.strictder = true; flags.strictder = true;
// checklocktimeverify (bip65) // CHECKLOCKTIMEVERIFY is now usable (bip65)
if (this.version >= 4 && prev.needsUpgrade(4)) if (this.version >= 4 && prev.isUpgraded(4))
flags.cltv = true; flags.cltv = true;
// Check for sigops limits
sigops = 0; sigops = 0;
for (i = 0; i < this.txs.length; i++) { for (i = 0; i < this.txs.length; i++) {
if (tx.sigops(true) > constants.script.maxTxSigops) tx = this.txs[i];
return false;
sigops += tx.sigops(strictp2sh); // Bitcoind does not check for this when accepting
if (sigops > constants.script.maxBlockSigops) // a block even though it probably should.
// if (tx.sigops(true) > constants.script.maxTxSigops) {
// // Block 71036 abused checksig to
// // include a huge number of sigops.
// if (!(network.type === 'main' && height === 71036))
// return false;
// }
// Start counting P2SH sigops once block
// timestamps reach March 31st, 2012.
if (this.ts >= constants.block.bip16time)
sigops += tx.sigops(true);
else
sigops += tx.sigops();
if (sigops > constants.script.maxBlockSigops) {
console.log('BAD SIGOPS: %s', height);
return false; return false;
}
}
// BIP30 - Ensure there are no duplicate txids
for (i = 0; i < this.txs.length; i++) {
tx = this.txs[i];
if (this.chain.index[tx.hash('hex')]) {
// Blocks 91842 and 91880 created duplicate
// txids by carefully crafting the coinbases.
if (!(network.type === 'main' && height === 91842 && height === 91880))
return false;
}
}
// If we are an ancestor of a checkpoint,
// we can skip the input verification.
if (height < network.checkpoints.lastHeight && !network.checkpoints[height])
flags.noScriptChecks = true;
// Verify the inputs of every tx (CheckInputs)
for (i = 0; i < this.txs.length; i++) {
tx = this.txs[i];
if (tx.isCoinbase())
continue;
if (flags.noScriptChecks)
continue;
for (j = 0; j < tx.inputs.length; j++) {
input = tx.inputs[j];
if (!input.out.tx)
continue;
assert(input.out.tx);
if (!tx.verify(j, true, flags))
return false;
// if (this.chain.isSpent(input.out.hash, input.out.index))
// return false;
}
} }
return true; return true;
}; };
Block.prototype.isGenesis = function isGenesis() {
return this.hash('hex') === utils.toHex(network.genesis._hash);
};
Block.prototype.getHeight = function getHeight() { Block.prototype.getHeight = function getHeight() {
if (!this.chain) if (!this.chain)
return -1; return -1;

View File

@ -600,16 +600,17 @@ Chain.prototype.height = function height() {
return this.getTip().height; return this.getTip().height;
}; };
// /home/chjj/bitcoin/src/pow.cpp
Chain.prototype.target = function target(last, block) { Chain.prototype.target = function target(last, block) {
var powLimit = utils.toCompact(network.powLimit); var powLimit = utils.toCompact(network.powLimit);
var interval = network.powTargetTimespan / network.powTargetSpacing | 0; var ts, first, i;
var first, ts;
// Genesis
if (!last) if (!last)
last = this.getTip(); return powLimit;
// Do not retarget // Do not retarget
if ((last.height + 1) % interval) { if ((last.height + 1) % network.powDiffInterval !== 0) {
if (network.powAllowMinDifficultyBlocks) { if (network.powAllowMinDifficultyBlocks) {
// Special behavior for testnet: // Special behavior for testnet:
ts = block ? (block.ts || block) : utils.now(); ts = block ? (block.ts || block) : utils.now();
@ -617,7 +618,7 @@ Chain.prototype.target = function target(last, block) {
return powLimit; return powLimit;
while (last.prev while (last.prev
&& last.height % interval !== 0 && last.height % network.powDiffInterval !== 0
&& last.bits !== powLimit) { && last.bits !== powLimit) {
last = last.prev; last = last.prev;
} }
@ -628,34 +629,39 @@ Chain.prototype.target = function target(last, block) {
} }
// Back 2 weeks // Back 2 weeks
first = this.index.entries[last.height - (interval - 1)]; // first = last;
// for (i = 0; first && i < network.powDiffInterval - 1; i++)
// first = first.prev;
if (!first) // Back 2 weeks
return 0; first = this.index.entries[last.height - (network.powDiffInterval - 1)];
assert(first);
return this.retarget(last, first); return this.retarget(last, first);
}; };
Chain.prototype.retarget = function retarget(last, first) { Chain.prototype.retarget = function retarget(last, first) {
var powTargetTimespan = new bn(network.powTargetTimespan); var powTargetTimespan = new bn(network.powTargetTimespan);
var actualTimespan, powLimit, target; var actualTimespan, target;
if (network.powNoRetargeting) if (network.powNoRetargeting)
return last.bits; return last.bits;
actualTimespan = new bn(last.ts).subn(first.ts); actualTimespan = new bn(last.ts - first.ts);
target = utils.fromCompact(last.bits);
if (actualTimespan.cmp(powTargetTimespan.divn(4)) < 0) if (actualTimespan.cmp(powTargetTimespan.divn(4)) < 0)
actualTimespan = powTargetTimespan.divn(4); actualTimespan = powTargetTimespan.divn(4);
if (actualTimespan.cmp(powTargetTimespan.muln(4)) > 0) if (actualTimespan.cmp(powTargetTimespan.muln(4)) > 0)
actualTimespan = powTargetTimespan.muln(4); actualTimespan = powTargetTimespan.muln(4);
powLimit = network.powLimit;
target = utils.fromCompact(last.bits);
target.imul(actualTimespan); target.imul(actualTimespan);
target = target.div(powTargetTimespan); target = target.div(powTargetTimespan);
if (target.cmp(powLimit) > 0)
target = powLimit.clone(); if (target.cmp(network.powLimit) > 0)
target = network.powLimit.clone();
return utils.toCompact(target); return utils.toCompact(target);
}; };
@ -832,13 +838,11 @@ ChainBlock.prototype.getMedianTime = function() {
}; };
ChainBlock.prototype.isOutdated = function(version) { ChainBlock.prototype.isOutdated = function(version) {
return this.isSuperMajority(version, return this.isSuperMajority(version, network.block.majorityRejectOutdated);
network.block.majorityRejectBlockOutdated);
}; };
ChainBlock.prototype.needsUpgrade = function(version) { ChainBlock.prototype.isUpgraded = function(version) {
return this.isSuperMajority(version, return this.isSuperMajority(version, network.block.majorityEnforceUpgrade);
network.block.majorityEnforceBlockUpgrade);
}; };
ChainBlock.prototype.isSuperMajority = function(version, required) { ChainBlock.prototype.isSuperMajority = function(version, required) {

View File

@ -100,6 +100,10 @@ Input.prototype.__defineGetter__('addrs', function() {
return this.data.addrs || []; return this.data.addrs || [];
}); });
Input.prototype.__defineGetter__('scriptaddr', function() {
return this.data.scriptaddr;
});
Input.prototype.__defineGetter__('m', function() { Input.prototype.__defineGetter__('m', function() {
return this.data.m || 1; return this.data.m || 1;
}); });

View File

@ -84,6 +84,10 @@ Output.prototype.__defineGetter__('addrs', function() {
return this.data.addrs || []; return this.data.addrs || [];
}); });
Output.prototype.__defineGetter__('scriptaddr', function() {
return this.data.scriptaddr;
});
Output.prototype.__defineGetter__('m', function() { Output.prototype.__defineGetter__('m', function() {
return this.data.m || 1; return this.data.m || 1;
}); });

View File

@ -467,6 +467,10 @@ Pool.prototype._handleBlocks = function _handleBlocks(hashes, peer) {
Pool.prototype._handleInv = function _handleInv(hashes, peer) { Pool.prototype._handleInv = function _handleInv(hashes, peer) {
var i, hash; var i, hash;
// Ignore for now if we're still syncing
if (!this.chain.isFull())
return;
for (i = 0; i < hashes.length; i++) { for (i = 0; i < hashes.length; i++) {
hash = utils.toHex(hashes[i]); hash = utils.toHex(hashes[i]);
if (!this.chain.has(hash)) { if (!this.chain.has(hash)) {

View File

@ -156,7 +156,15 @@ exports.block = {
maxSize: 1000000, maxSize: 1000000,
maxSigops: 1000000 / 50, maxSigops: 1000000 / 50,
maxOrphanTx: 1000000 / 100, maxOrphanTx: 1000000 / 100,
medianTimeSpan: 11 medianTimeSpan: 11,
bip16time: 1333238400
};
exports.tx = {
maxSize: 100000,
fee: 10000,
dust: 5460,
bareMultisig: true
}; };
exports.script = { exports.script = {
@ -167,7 +175,9 @@ exports.script = {
maxPubkeysPerMultisig: 20, maxPubkeysPerMultisig: 20,
maxBlockSigops: exports.block.maxSize / 50, maxBlockSigops: exports.block.maxSize / 50,
maxScripthashSigops: 15, maxScripthashSigops: 15,
maxTxSigops: exports.block.maxSize / 50 / 5 maxTxSigops: exports.block.maxSize / 50 / 5,
maxOpReturnBytes: 83,
maxOpReturn: 80
}; };
exports.reject = { exports.reject = {

View File

@ -80,6 +80,7 @@ main.checkpoints = main.checkpoints.reduce(function(out, block) {
main.checkpoints.tsLastCheckpoint = 1397080064; main.checkpoints.tsLastCheckpoint = 1397080064;
main.checkpoints.txsLastCheckpoint = 36544669; main.checkpoints.txsLastCheckpoint = 36544669;
main.checkpoints.txsPerDay = 60000.0; main.checkpoints.txsPerDay = 60000.0;
main.checkpoints.lastHeight = Object.keys(main.checkpoints).sort().pop();
main.halvingInterval = 210000; main.halvingInterval = 210000;
@ -137,12 +138,13 @@ main.powLimit = new bn(
); );
main.powTargetTimespan = 14 * 24 * 60 * 60; // two weeks main.powTargetTimespan = 14 * 24 * 60 * 60; // two weeks
main.powTargetSpacing = 10 * 60; main.powTargetSpacing = 10 * 60;
main.powDiffInterval = main.powTargetTimespan / main.powTargetSpacing | 0;
main.powAllowMinDifficultyBlocks = false; main.powAllowMinDifficultyBlocks = false;
main.powNoRetargeting = false; main.powNoRetargeting = false;
main.block = { main.block = {
majorityEnforceBlockUpgrade: 750, majorityEnforceUpgrade: 750,
majorityRejectBlockOutdated: 950, majorityRejectOutdated: 950,
majorityWindow: 1000 majorityWindow: 1000
}; };
@ -193,6 +195,7 @@ testnet.checkpoints = testnet.checkpoints.reduce(function(out, block) {
testnet.checkpoints.tsLastCheckpoint = 1338180505; testnet.checkpoints.tsLastCheckpoint = 1338180505;
testnet.checkpoints.txsLastCheckpoint = 16341; testnet.checkpoints.txsLastCheckpoint = 16341;
testnet.checkpoints.txsPerDay = 300; testnet.checkpoints.txsPerDay = 300;
testnet.checkpoints.lastHeight = Object.keys(testnet.checkpoints).sort().pop();
testnet.halvingInterval = 210000; testnet.halvingInterval = 210000;
@ -241,11 +244,12 @@ testnet.powLimit = new bn(
); );
testnet.powTargetTimespan = 14 * 24 * 60 * 60; // two weeks testnet.powTargetTimespan = 14 * 24 * 60 * 60; // two weeks
testnet.powTargetSpacing = 10 * 60; testnet.powTargetSpacing = 10 * 60;
testnet.powDiffInterval = testnet.powTargetTimespan / testnet.powTargetSpacing | 0;
testnet.powAllowMinDifficultyBlocks = true; testnet.powAllowMinDifficultyBlocks = true;
testnet.powNoRetargeting = false; testnet.powNoRetargeting = false;
testnet.block = { testnet.block = {
majorityEnforceBlockUpgrade: 51, majorityEnforceUpgrade: 51,
majorityRejectBlockOutdated: 75, majorityRejectOutdated: 75,
majorityWindow: 100 majorityWindow: 100
}; };

View File

@ -232,9 +232,12 @@ script._next = function _next(to, s, pc) {
return -1; return -1;
}; };
script.execute = function execute(s, stack, tx, index, recurse) { script.execute = function execute(s, stack, tx, index, flags, recurse) {
s = s.slice(); s = s.slice();
if (!flags)
flags = {};
if (s.length > constants.script.maxOps) if (s.length > constants.script.maxOps)
return false; return false;
@ -806,7 +809,7 @@ script.execute = function execute(s, stack, tx, index, recurse) {
} }
case 'eval_': { case 'eval_': {
// OP_EVAL = OP_NOP1 // OP_EVAL = OP_NOP1
if (!script.allowEval) if (!flags.allowEval)
break; break;
recurse = recurse || 0; recurse = recurse || 0;
@ -828,7 +831,7 @@ script.execute = function execute(s, stack, tx, index, recurse) {
if (res) if (res)
return false; return false;
res = script.execute(evalScript, stack, tx, index, recurse); res = script.execute(evalScript, stack, tx, index, flags, recurse);
if (!res) if (!res)
return false; return false;
@ -847,13 +850,13 @@ script.execute = function execute(s, stack, tx, index, recurse) {
return true; return true;
}; };
script.exec = function exec(input, output, tx, i, recurse) { script.exec = function exec(input, output, tx, i, flags) {
var stack = []; var stack = [];
var res; var res;
script.execute(input, stack, tx, i, recurse); script.execute(input, stack, tx, i, flags);
res = script.execute(output, stack, tx, i, recurse); res = script.execute(output, stack, tx, i, flags);
if (!res || stack.length === 0 || new bn(stack.pop()).cmpn(0) === 0) if (!res || stack.length === 0 || new bn(stack.pop()).cmpn(0) === 0)
return false; return false;
@ -888,6 +891,34 @@ script.standard = function standard(s) {
|| null; || null;
}; };
script.isStandard = function isStandard(s) {
var m, n;
var type = script.standard(s);
if (!type)
return false;
if (type === 'multisig') {
m = new bn(s[0]).toNumber();
n = new bn(s[s.length - 2]).toNumber();
if (n < 1 || n > 3)
return false;
if (m < 1 || m > n)
return false;
} else if (type === 'colored') {
if (script.size(s) > constants.script.maxOpReturnBytes)
return false;
}
return type != null;
};
script.size = function size(s) {
if (s._raw)
return s._raw.length;
return bcoin.script.encode(s).length;
};
script.lockTime = function lockTime(s) { script.lockTime = function lockTime(s) {
var lock = s[0]; var lock = s[0];
var res = s.length > 3 var res = s.length > 3
@ -1053,7 +1084,7 @@ script.isColored = function isColored(s) {
return s[0] === 'ret' return s[0] === 'ret'
&& Array.isArray(s[1]) && Array.isArray(s[1])
&& s[1].length <= 40; && s[1].length <= constants.script.maxOpReturn;
}; };
script.colored = function colored(s) { script.colored = function colored(s) {
@ -1411,6 +1442,8 @@ script.pushOnly = function pushOnly(s) {
op = s[i]; op = s[i];
if (Array.isArray(op) || (op >= 1 && op <= 16)) if (Array.isArray(op) || (op >= 1 && op <= 16))
continue; continue;
if (constants.opcodes[op] == null)
return false;
return false; return false;
} }
return true; return true;
@ -1425,6 +1458,8 @@ script.sigops = function sigops(s, accurate) {
op = s[i]; op = s[i];
if (Array.isArray(op)) if (Array.isArray(op))
continue; continue;
if (constants.opcodes[op] == null)
return 0;
if (op === 'checksig' || op === 'checksigverify') { if (op === 'checksig' || op === 'checksigverify') {
n++; n++;
} else if (op === 'checkmultisig' || op === 'checkmultisigverify') { } else if (op === 'checkmultisig' || op === 'checkmultisigverify') {
@ -1435,14 +1470,12 @@ script.sigops = function sigops(s, accurate) {
} }
} }
lastOp = op; lastOp = op;
if (lastOp === null)
return 0;
} }
return n; return n;
}; };
script.sigopsScripthash = function sigopsScripthash(s, accurate) { script.sigopsScripthash = function sigopsScripthash(s) {
if (!script.isScripthashInput(s)) if (!script.isScripthashInput(s))
return 0; return 0;
@ -1451,5 +1484,40 @@ script.sigopsScripthash = function sigopsScripthash(s, accurate) {
s = script.decode(input[input.length - 1]); s = script.decode(input[input.length - 1]);
return script.sigops(s, accurate); return script.sigops(s, true);
};
script.args = function args(s) {
var type, pubs, m;
s = bcoin.script.subscript(s);
if (script.lockTime(s))
s = s.slice(3);
type = script.standard(s);
if (type === 'pubkey')
return 1;
if (type === 'pubkeyhash')
return 2;
if (type === 'multisig') {
pubs = bcoin.script.isMultisig(s);
if (!pub)
return -1;
m = new bn(s[0]).toNumber();
if (pubs.length < 1 || m < 1)
return -1;
return m + 1;
}
if (this.type === 'scripthash')
return 1;
if (this.type === 'colored')
return -1;
return -1;
}; };

View File

@ -67,8 +67,8 @@ function TX(data, block) {
this.ps = this.ts === 0 ? utils.now() : 0; this.ps = this.ts === 0 ? utils.now() : 0;
} }
TX.fee = 10000; TX.fee = constants.tx.fee;
TX.dust = 5460; TX.dust = constants.tx.dust;
TX.prototype.clone = function clone() { TX.prototype.clone = function clone() {
return new TX(this); return new TX(this);
@ -514,7 +514,7 @@ TX.prototype.subscriptHash = function subscriptHash(index, s, type) {
return hash; return hash;
}; };
TX.prototype.verify = function verify(index, force) { TX.prototype.verify = function verify(index, force, flags) {
// Valid if included in block // Valid if included in block
if (!force && this.ts !== 0) if (!force && this.ts !== 0)
return true; return true;
@ -543,8 +543,8 @@ TX.prototype.verify = function verify(index, force) {
return false; return false;
} }
bcoin.script.execute(input.script, stack, this, i); bcoin.script.execute(input.script, stack, this, i, flags);
res = bcoin.script.execute(prev, stack, this, i); res = bcoin.script.execute(prev, stack, this, i, flags);
if (!res || stack.length === 0 || new bn(stack.pop()).cmpn(0) === 0) if (!res || stack.length === 0 || new bn(stack.pop()).cmpn(0) === 0)
return false; return false;
@ -554,7 +554,7 @@ TX.prototype.verify = function verify(index, force) {
if (!Array.isArray(redeem)) if (!Array.isArray(redeem))
return false; return false;
redeem = bcoin.script.decode(redeem); redeem = bcoin.script.decode(redeem);
res = bcoin.script.execute(redeem, stack, this, i); res = bcoin.script.execute(redeem, stack, this, i, flags);
if (!res || stack.length === 0 || new bn(stack.pop()).cmpn(0) === 0) if (!res || stack.length === 0 || new bn(stack.pop()).cmpn(0) === 0)
return false; return false;
} }
@ -881,19 +881,126 @@ TX.prototype._isFinal = function _isFinal(height, ts) {
return true; return true;
}; };
TX.prototype.sigops = function sigops(scripthash) { TX.prototype.sigops = function sigops(scripthash, accurate) {
var n = 0; var n = 0;
this.inputs.forEach(function(input) { this.inputs.forEach(function(input) {
n += bcoin.script.sigops(input.script); n += bcoin.script.sigops(input.script, accurate);
if (scripthash) if (scripthash && !this.isCoinbase())
n += bcoin.script.sigopsScripthash(input.script); n += bcoin.script.sigopsScripthash(input.script);
}); }, true);
this.outputs.forEach(function(output) { this.outputs.forEach(function(output) {
n += bcoin.script.sigops(output.script); n += bcoin.script.sigops(output.script, accurate);
}); }, this);
return n; return n;
}; };
TX.prototype.isStandard = function isStandard() {
var i, input, output, type;
var colored = 0;
if (this.version > constants.tx.version || this.version < 1)
return false;
if (this.size() > constants.tx.maxSize)
return false;
for (i = 0; i < this.inputs.length; i++) {
input = this.inputs[i];
if (script.size(input.script) > 1650)
return false;
if (!bcoin.script.pushOnly(input.script))
return false;
}
for (i = 0; i < this.outputs.length; i++) {
output = this.outputs[i];
type = bcoin.script.standard(output.script);
if (!bcoin.script.isStandard(output.script))
return false;
if (!type)
return false;
if (type === 'colored') {
colored++;
continue;
}
if (type === 'multisig' && !constants.tx.bareMultisig)
return false;
if (output.value.cmpn(constants.tx.dust) < 0)
return false;
}
if (colored > 1)
return false;
return true;
};
TX.prototype.isStandardInputs = function isStandardInputs(flags) {
var i, input, prev, args, stack, res, s, targs;
if (this.isCoinbase())
return true;
for (i = 0; i < this.inputs.length; i++) {
input = this.inputs[i];
if (!input.out.tx)
return false;
prev = input.out.tx[input.out.index];
if (!prev)
return false;
args = bcoin.script.args(prev.script);
if (args < 0)
return false;
stack = [];
res = bcoin.script.execute(input.script, stack, this, i, flags);
if (!res)
return false;
if (bcoin.script.isScripthash(prev.script)) {
if (stack.length === 0)
return false;
s = stack[stack.length - 1];
if (!Array.isArray(s))
return false;
s = bcoin.script.decode(s);
if (bcoin.script.standard(s)) {
targs = bcoin.script.args(s);
if (targs < 0)
return false;
args += targs;
} else {
// Bitcoind returns here whether true
// or false... strange behavior (bug?)
return script.sigops(s, true) <= constants.script.maxScripthashSigops;
}
}
if (stack.length !== args)
return false;
}
return true;
};
TX.prototype.getHeight = function getHeight() { TX.prototype.getHeight = function getHeight() {
if (!this.chain) if (!this.chain)
return -1; return -1;

View File

@ -787,6 +787,10 @@ utils.fromCompact = function fromCompact(compact) {
if (negative) if (negative)
num.ineg(); num.ineg();
// var overflow = mantissa !== 0 && ((exponent > 34)
// || (mantissa > 0xff && exponent > 33)
// || (mantissa > 0xffff && exponent > 32));
return num; return num;
}; };

View File

@ -12,7 +12,7 @@ var pool = bcoin.pool({
redundancy: 1, redundancy: 1,
parallel: 4000, parallel: 4000,
loadWindow: 750, loadWindow: 750,
createConnection: function() { createConnection_: function() {
console.log('connecting...'); console.log('connecting...');
return net.connect(8333, addrs[(Math.random() * addrs.length) | 0]); return net.connect(8333, addrs[(Math.random() * addrs.length) | 0]);
} }