sigop counting. consensus stuff.

This commit is contained in:
Christopher Jeffrey 2016-04-27 14:44:58 -07:00
parent 6e2b58c16d
commit f27cde4159
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
3 changed files with 182 additions and 84 deletions

View File

@ -846,7 +846,6 @@ Parser.parseBlockHeaders = function parseBlockHeaders(p) {
Parser.parseBlock = function parseBlock(p) {
var txs = [];
var witnessSize = 0;
var version, prevBlock, merkleRoot, ts, bits, nonce;
var i, totalTX, tx;
@ -863,10 +862,11 @@ Parser.parseBlock = function parseBlock(p) {
for (i = 0; i < totalTX; i++) {
tx = Parser.parseTX(p);
witnessSize += tx._witnessSize;
txs.push(tx);
}
p.end();
return {
version: version,
prevBlock: prevBlock,
@ -874,9 +874,7 @@ Parser.parseBlock = function parseBlock(p) {
ts: ts,
bits: bits,
nonce: nonce,
txs: txs,
_witnessSize: witnessSize,
_size: p.end()
txs: txs
};
};
@ -1105,7 +1103,6 @@ Parser.parseTX = function parseTX(p) {
var inCount, inputs;
var outCount, outputs;
var version, locktime, i;
var raw;
if (Parser.isWitnessTX(p))
return Parser.parseWitnessTX(p);
@ -1129,18 +1126,14 @@ Parser.parseTX = function parseTX(p) {
locktime = p.readU32();
raw = p.endData();
p.end();
return {
version: version,
flag: 1,
inputs: inputs,
outputs: outputs,
locktime: locktime,
_witnessSize: 0,
_raw: raw,
_size: raw.length
// _size: p.end()
locktime: locktime
};
};
@ -1175,8 +1168,6 @@ Parser.parseWitnessTX = function parseWitnessTX(p) {
var outCount, outputs;
var marker, flag;
var version, locktime, i;
var witnessSize = 0;
var raw;
p = new BufferReader(p);
p.start();
@ -1207,23 +1198,18 @@ Parser.parseWitnessTX = function parseWitnessTX(p) {
for (i = 0; i < inCount; i++) {
witness = Parser.parseWitness(p);
inputs[i].witness = witness;
witnessSize += witness._size;
}
locktime = p.readU32();
raw = p.endData();
p.end();
return {
version: version,
flag: flag,
inputs: inputs,
outputs: outputs,
locktime: locktime,
_raw: raw,
_size: raw.length,
// _size: p.end(),
_witnessSize: witnessSize + 2
locktime: locktime
};
};

View File

@ -2187,10 +2187,12 @@ Script.createWitnessProgram = function createWitnessProgram(version, data) {
*/
Script.createCommitment = function createCommitment(hash) {
assert(hash.length === 32);
var p = new BufferWriter();
p.writeU32BE(0xaa21a9ed);
p.writeHash(hash);
return new Script([
opcodes.OP_RETURN,
Buffer.concat([new Buffer([0xaa, 0x21, 0xa9, 0xed]), hash])
p.render()
]);
};
@ -2203,8 +2205,11 @@ Script.prototype.getRedeem = function getRedeem() {
if (this.mutable)
return Script.getRedeem(this.code);
if (!this.redeem)
if (!this.redeem) {
if (!this.isPushOnly())
return;
this.redeem = Script.getRedeem(this.code);
}
return this.redeem;
};
@ -2218,6 +2223,15 @@ Script.prototype.getRedeem = function getRedeem() {
Script.getRedeem = function getRedeem(code) {
var redeem = code[code.length - 1];
if (typeof redeem === 'number') {
if (redeem > opcodes.OP_16)
return;
return new Script([redeem]);
}
if (Script.isBadPush(redeem))
return;
if (!Buffer.isBuffer(redeem))
return;
@ -3423,6 +3437,9 @@ Script.prototype.getSigops = function getSigops(accurate) {
var lastOp = -1;
var i, op;
if (flags == null)
flags = constants.flags.STANDARD_VERIFY_FLAGS;
for (i = 0; i < this.code.length; i++) {
op = this.code[i];
@ -3447,6 +3464,100 @@ Script.prototype.getSigops = function getSigops(accurate) {
return total;
};
/**
* Count the sigops in the script, taking into account redeem scripts.
* @param {Script} input - Input script, needed for access to redeem script.
* @returns {Number} sigop count
*/
Script.prototype.getScripthashSigops = function getScripthashSigops(input) {
var i, op, redeem;
if (!this.isScripthash())
return this.getSigops(true);
for (i = 0; i < input.code.length; i++) {
op = input.code[i];
if (Buffer.isBuffer(op))
continue;
if (Script.isBadPush(op))
return 0;
if (op > opcodes.OP_16)
return 0;
}
if (typeof op === 'number')
return 0;
redeem = new Script(op);
return redeem.getSigops(true);
};
/**
* Count the sigops for a program.
* @param {Program} program
* @param {Witness} witness
* @param {VerifyFlags} flags
* @returns {Number} sigop count
*/
Script.witnessSigops = function witnessSigops(program, witness, flags) {
if (flags == null)
flags = constants.flags.STANDARD_VERIFY_FLAGS;
if (program.version === 0) {
if (program.data.length === 20)
return 1;
if (program.data.length === 32 && witness.items.length > 0) {
redeem = witness.getRedeem();
return redeem.getSigops(true);
}
}
return 0;
};
/**
* Count the sigops in a script, taking into account witness programs.
* @param {Script} input
* @param {Script} output
* @param {Witness} witness
* @param {VerifyFlags} flags
* @returns {Number} sigop count
*/
Script.getWitnessSigops = function getWitnessSigops(input, output, witness, flags) {
var redeem;
if (flags == null)
flags = constants.flags.STANDARD_VERIFY_FLAGS;
if ((flags & constants.flags.VERIFY_WITNESS) === 0)
return 0;
assert((flags & constants.flags.VERIFY_P2SH) !== 0);
if (output.isWitnessProgram())
return Script.witnessSigops(output.getWitnessProgram(), witness, flags);
// This is a unique situation in terms of consensus
// rules. We can just grab the redeem script without
// "parsing" (i.e. checking for pushdata parse errors)
// the script. This is because isPushOnly is called
// which checks for parse errors and will return
// false if one is found. Even the bitcoind code
// does not check the return value of GetOp.
if (output.isScripthash() && input.isPushOnly()) {
redeem = input.getRedeem();
if (redeem.isWitnessProgram())
return Script.witnessSigops(redeem.getWitnessProgram(), witness, flags);
}
return 0;
};
/**
* Calculate the number of expected "arguments" (pushdata
* ops in the input script) for an output script. Used for
@ -3728,7 +3839,7 @@ Script.verify = function verify(input, witness, output, tx, i, flags) {
// If we had a witness but no witness program, fail.
if (flags & constants.flags.VERIFY_WITNESS) {
assert((flags & constants.flags.VERIFY_P2SH) !== 0);
if (!hadWitness && witness.length > 0)
if (!hadWitness && witness.items.length > 0)
throw new ScriptError('WITNESS_UNEXPECTED');
}
@ -4086,10 +4197,7 @@ Script.encode = function encode(code, writer) {
p.writeU8(opcodes.OP_0);
} else if (op.length <= 0x4b) {
if (op.length === 1) {
if (op[0] === 0) {
p.writeU8(opcodes.OP_0);
continue;
} else if (op[0] >= 1 && op[0] <= 16) {
if (op[0] >= 1 && op[0] <= 16) {
p.writeU8(op[0] + 0x50);
continue;
} else if (op[0] === 0x81) {

View File

@ -877,83 +877,87 @@ TX.prototype.isFinal = function isFinal(height, ts) {
return true;
};
TX.prototype._getSigops = function _getSigops(scriptHash, accurate) {
/**
* Calculate legacy (inaccurate) sigop count.
* @returns {Number} sigop count
*/
TX.prototype.getLegacySigops = function getLegacySigops() {
var total = 0;
var i, input, output, prev;
var i;
for (i = 0; i < this.inputs.length; i++) {
input = this.inputs[i];
for (i = 0; i < tx.inputs.length; i++)
total += tx.inputs[i].script.getSigops(false);
total += input.script.getSigops(accurate);
for (i = 0; i < tx.outputs.length; i++)
total += tx.outputs[i].script.getSigops(false);
if (!input.coin)
continue;
return total;
};
prev = input.coin.script;
/**
* Calculate accurate sigop count, taking into account redeem scripts.
* @returns {Number} sigop count
*/
if (scriptHash && !this.isCoinbase()) {
if (!prev.isScripthash())
continue;
TX.prototype.getScripthashSigops = function getScripthashSigops() {
var total = 0;
var i, input;
if (!input.script.isPushOnly())
continue;
if (this.isCoinbase())
return 0;
prev = input.script.getRedeem();
if (!prev)
continue;
total += prev.getSigops(true);
}
}
for (i = 0; i < this.outputs.length; i++) {
output = this.outputs[i];
total += output.script.getSigops(accurate);
for (i = 0; i < tx.inputs.length; i++) {
input = tx.inputs[i];
if (input.coin.script.isScripthash())
total += input.coin.script.getSigops(input.script);
}
return total;
};
/**
* Calculate virtual sigop count. First calculates
* the traditional amount of sigops as cost and takes
* into account sigops for witness programs.
* @param {Boolean?} scriptHash - Whether to count redeem script sigops.
* @param {Boolean?} accurate - Whether to count CHECKMULTISIG(VERIFY)
* ops accurately.
* @returns {Number} sigop count
* Calculate sigops cost, taking into account witness programs.
* @param {VerifyFlags?} flags
* @returns {Number} sigop cost
*/
TX.prototype.getSigops = function getSigops(scriptHash, accurate) {
var cost = this._getSigops(scriptHash, accurate) * 4;
var i, input, output, prev;
TX.prototype.getSigopsCost = function getSigopsCost(flags) {
var cost = this.getLegacySigops() * 4;
var input, i;
if (flags == null)
flags = constants.flags.STANDARD_VERIFY_FLAGS;
if (this.isCoinbase())
return cost;
if (flags & constants.flags.VERIFY_P2SH)
cost += this.getScripthashSigops() * 4;
for (i = 0; i < this.inputs.length; i++) {
input = this.inputs[i];
if (!input.coin)
continue;
prev = input.coin.script;
if (prev.isScripthash())
prev = input.script.getRedeem();
if (prev && prev.isWitnessScripthash()) {
prev = input.witness.getRedeem();
if (prev)
cost += prev.getSigops(true);
}
cost += Script.getWitnessSigops(
input.script,
input.coin.script,
input.witness,
flags);
}
for (i = 0; i < this.outputs.length; i++) {
output = this.outputs[i];
if (output.script.isWitnessPubkeyhash())
cost += 1;
}
return cost;
};
return (cost + 3) / 4 | 0;
/**
* Calculate virtual sigop count.
* @param {VerifyFlags?} flags
* @returns {Number} sigop count
*/
TX.prototype.getSigops = function getSigops(flags) {
if (flags == null)
flags = constants.flags.STANDARD_VERIFY_FLAGS;
return (this.getSigopsCost(flags) + 3) / 4 | 0;
};
/**