From e1ec89fe7dce99b59eb40deb7f763aaae788d02d Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Wed, 6 Jan 2016 01:36:38 -0800 Subject: [PATCH] sigops --- lib/bcoin/block.js | 27 ++++++++++-------- lib/bcoin/protocol/constants.js | 6 +++- lib/bcoin/script.js | 49 +++++++++++++++++++++++++++++++++ lib/bcoin/tx.js | 20 +++++++++++--- 4 files changed, 85 insertions(+), 17 deletions(-) diff --git a/lib/bcoin/block.js b/lib/bcoin/block.js index 684c39b1..f086ca47 100644 --- a/lib/bcoin/block.js +++ b/lib/bcoin/block.js @@ -68,15 +68,6 @@ function Block(data, subtype) { tx.ts = tx.ts || self.ts; return tx; }); - - // if (this.version >= 2 && this.height === -1) { - // tx = this.txs[0]; - // if (tx && tx.isCoinbase()) { - // height = bcoin.script.coinbaseHeight(tx.inputs[0].script, this); - // if (height > 0) - // this.height = height; - // } - // } } this.verify(); @@ -280,7 +271,10 @@ Block.prototype._verify = function _verify() { }; Block.prototype.postVerify = function postVerify() { - var prev, i, tx, cb; + var prev, i, tx, cb, sigops; + var flags = {}; + var bip16time = 1333238400; + var strictp2sh = this.ts >= bip16time; if (this.subtype !== 'block') return true; @@ -332,11 +326,20 @@ Block.prototype.postVerify = function postVerify() { // sig validation (bip66) if (this.version >= 3 && prev.needsUpgrade(3)) - this.scriptFlags |= 1; // dersig + flags.strictder = true; // checklocktimeverify (bip65) if (this.version >= 4 && prev.needsUpgrade(4)) - this.scriptFlags |= 2; + flags.cltv = true; + + sigops = 0; + for (i = 0; i < this.txs.length; i++) { + if (tx.sigops(true) > constants.script.maxTxSigops) + return false; + sigops += tx.sigops(strictp2sh); + if (sigops > constants.script.maxBlockSigops) + return false; + } return true; }; diff --git a/lib/bcoin/protocol/constants.js b/lib/bcoin/protocol/constants.js index 2717d4ca..f74594ce 100644 --- a/lib/bcoin/protocol/constants.js +++ b/lib/bcoin/protocol/constants.js @@ -163,7 +163,11 @@ exports.script = { maxSize: 10000, maxStack: 1000, maxPush: 520, - maxOps: 201 + maxOps: 201, + maxPubkeysPerMultisig: 20, + maxBlockSigops: exports.block.maxSize / 50, + maxScripthashSigops: 15, + maxTxSigops: exports.block.maxSize / 50 / 5 }; exports.reject = { diff --git a/lib/bcoin/script.js b/lib/bcoin/script.js index 4fd5c496..dd95cb2e 100644 --- a/lib/bcoin/script.js +++ b/lib/bcoin/script.js @@ -1404,3 +1404,52 @@ script.format = function format(input, output) { return scripts; }; + +script.pushOnly = function pushOnly(s) { + var i, op; + for (i = 0; i < s.length; i++) { + op = s[i]; + if (Array.isArray(op) || (op >= 1 && op <= 16)) + continue; + return false; + } + return true; +}; + +script.sigops = function sigops(s, accurate) { + var i, op; + var n = 0; + var lastOp = -1; + + for (i = 0; i < s.length; i++) { + op = s[i]; + if (Array.isArray(op)) + continue; + if (op === 'checksig' || op === 'checksigverify') { + n++; + } else if (op === 'checkmultisig' || op === 'checkmultisigverify') { + if (accurate && lastOp >= 1 && lastOp <= 16) { + n += lastOp; + } else { + n += constants.script.maxPubkeysPerMultisig; + } + } + lastOp = op; + if (lastOp === null) + return 0; + } + + return n; +}; + +script.sigopsScripthash = function sigopsScripthash(s, accurate) { + if (!script.isScripthashInput(s)) + return 0; + + if (!script.pushOnly(input)) + return 0; + + s = script.decode(input[input.length - 1]); + + return script.sigops(s, accurate); +}; diff --git a/lib/bcoin/tx.js b/lib/bcoin/tx.js index a6c658f5..d42666f5 100644 --- a/lib/bcoin/tx.js +++ b/lib/bcoin/tx.js @@ -537,10 +537,9 @@ TX.prototype.verify = function verify(index, force) { prev = input.out.tx.outputs[input.out.index].script; if (bcoin.script.isScripthash(prev)) { - // p2sh transactions cannot have anything - // other than pushdata ops in the scriptSig - push = input.script.slice(1).every(Array.isArray); - if (!push) + // P2SH transactions cannot have anything + // other than pushdata ops in the scriptSig. + if (!bcoin.script.pushOnly(input.script)) return false; } @@ -882,6 +881,19 @@ TX.prototype._isFinal = function _isFinal(height, ts) { return true; }; +TX.prototype.sigops = function sigops(scripthash) { + var n = 0; + this.inputs.forEach(function(input) { + n += bcoin.script.sigops(input.script); + if (scripthash) + n += bcoin.script.sigopsScripthash(input.script); + }); + this.outputs.forEach(function(output) { + n += bcoin.script.sigops(output.script); + }); + return n; +}; + TX.prototype.getHeight = function getHeight() { if (!this.chain) return -1;