sigop counting. consensus stuff.
This commit is contained in:
parent
6e2b58c16d
commit
f27cde4159
@ -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
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
114
lib/bcoin/tx.js
114
lib/bcoin/tx.js
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Loading…
Reference in New Issue
Block a user