more block verification.
This commit is contained in:
parent
e1ec89fe7d
commit
8d120a4fd6
@ -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;
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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;
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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;
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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)) {
|
||||||
|
|||||||
@ -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 = {
|
||||||
|
|||||||
@ -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
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
131
lib/bcoin/tx.js
131
lib/bcoin/tx.js
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -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]);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user