stop using vsize. switch to cost.
This commit is contained in:
parent
c312550429
commit
e258755671
@ -125,13 +125,24 @@ Block.prototype._getSize = function _getSize() {
|
||||
*/
|
||||
|
||||
Block.prototype.getVirtualSize = function getVirtualSize(force) {
|
||||
var scale = constants.WITNESS_SCALE_FACTOR;
|
||||
return (this.getCost() + scale - 1) / scale | 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate block cost.
|
||||
* @param {Boolean?} force - If true, always recalculate.
|
||||
* @returns {Number} cost
|
||||
*/
|
||||
|
||||
Block.prototype.getCost = function getCost(force) {
|
||||
var size, witnessSize, base;
|
||||
|
||||
size = this.getSize(force);
|
||||
witnessSize = this.getWitnessSize(force);
|
||||
base = size - witnessSize;
|
||||
|
||||
return (base * 4 + witnessSize + 3) / 4 | 0;
|
||||
return base * (constants.WITNESS_SCALE_FACTOR - 1) + size;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -146,6 +157,18 @@ Block.prototype.getSize = function getSize(force) {
|
||||
return this._size;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get base block size (without witness).
|
||||
* @param {Boolean?} force - If true, always recalculate.
|
||||
* @returns {Number} size
|
||||
*/
|
||||
|
||||
Block.prototype.getBaseSize = function getBaseSize(force) {
|
||||
if (force || this._size === 0)
|
||||
this._getSize();
|
||||
return this._size - this._witnessSize;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the total size of the witnesses.
|
||||
* @param {Boolean?} force - If true, always recalculate.
|
||||
@ -202,24 +225,6 @@ Block.prototype.indexOf = function indexOf(hash) {
|
||||
return -1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Count sigops taking into account the cost vs. witness ops.
|
||||
* @param {Boolean?} scriptHash - Whether to count redeem script ops.
|
||||
* @param {Boolean?} accurate - Whether to use accurate counting
|
||||
* for CHECKMULTISIG(VERIFY).
|
||||
* @returns {Number} sigops
|
||||
*/
|
||||
|
||||
Block.prototype.getSigops = function getSigops(scriptHash, accurate) {
|
||||
var total = 0;
|
||||
var i;
|
||||
|
||||
for (i = 0; i < this.txs.length; i++)
|
||||
total += this.txs[i].getSigops(scriptHash, accurate);
|
||||
|
||||
return total;
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate merkle root.
|
||||
* @param {String?} enc - Encoding, can be `'hex'` or null.
|
||||
@ -327,6 +332,7 @@ Block.prototype.__defineGetter__('commitmentHash', function() {
|
||||
|
||||
Block.prototype._verify = function _verify(ret) {
|
||||
var sigops = 0;
|
||||
var scale = constants.WITNESS_SCALE_FACTOR;
|
||||
var i, tx, merkle;
|
||||
|
||||
if (!ret)
|
||||
@ -337,7 +343,7 @@ Block.prototype._verify = function _verify(ret) {
|
||||
|
||||
// Size can't be bigger than MAX_BLOCK_SIZE
|
||||
if (this.txs.length > constants.block.MAX_SIZE
|
||||
|| this.getVirtualSize() > constants.block.MAX_SIZE) {
|
||||
|| this.getBaseSize() > constants.block.MAX_SIZE) {
|
||||
ret.reason = 'bad-blk-length';
|
||||
ret.score = 100;
|
||||
return false;
|
||||
@ -366,13 +372,13 @@ Block.prototype._verify = function _verify(ret) {
|
||||
return false;
|
||||
|
||||
// Count legacy sigops (do not count scripthash or witness)
|
||||
// sigops += tx._getSigops();
|
||||
// if (sigops > constants.block.MAX_SIGOPS) {
|
||||
// return callback(new VerifyError(block,
|
||||
// 'invalid',
|
||||
// 'bad-blk-sigops',
|
||||
// 100));
|
||||
// }
|
||||
sigops += tx.getLegacySigops();
|
||||
if (sigops * scale > constants.block.MAX_SIGOPS_COST) {
|
||||
return callback(new VerifyError(block,
|
||||
'invalid',
|
||||
'bad-blk-sigops',
|
||||
100));
|
||||
}
|
||||
}
|
||||
|
||||
// Check merkle root
|
||||
|
||||
@ -555,6 +555,15 @@ Chain.prototype._verify = function _verify(block, prev, callback) {
|
||||
}
|
||||
}
|
||||
|
||||
// Check block cost (different from block size
|
||||
// check in non-contextual verification).
|
||||
if (block.getCost() > constants.block.MAX_COST) {
|
||||
return done(new VerifyError(block,
|
||||
'invalid',
|
||||
'bad-blk-cost',
|
||||
100));
|
||||
}
|
||||
|
||||
// Get timestamp for tx.isFinal().
|
||||
ts = (state.lockFlags & constants.flags.MEDIAN_TIME_PAST) !== 0
|
||||
? medianTime
|
||||
@ -819,21 +828,6 @@ Chain.prototype._checkInputs = function _checkInputs(block, prev, flags, callbac
|
||||
tx = block.txs[i];
|
||||
hash = tx.hash('hex');
|
||||
|
||||
// Check for block sigops limits
|
||||
// Start counting P2SH sigops once block
|
||||
// timestamps reach March 31st, 2012.
|
||||
if (flags & constants.flags.VERIFY_P2SH)
|
||||
sigops += tx.getSigops(true);
|
||||
else
|
||||
sigops += tx.getSigops();
|
||||
|
||||
if (sigops > constants.block.MAX_SIGOPS) {
|
||||
return callback(new VerifyError(block,
|
||||
'invalid',
|
||||
'bad-blk-sigops',
|
||||
100));
|
||||
}
|
||||
|
||||
// Coinbases do not have prevouts
|
||||
if (tx.isCoinbase())
|
||||
continue;
|
||||
@ -876,6 +870,16 @@ Chain.prototype._checkInputs = function _checkInputs(block, prev, flags, callbac
|
||||
}
|
||||
}
|
||||
|
||||
// Count sigops (legacy + scripthash? + witness?)
|
||||
sigops += tx.getSigopsCost(flags);
|
||||
|
||||
if (sigops > constants.block.MAX_SIGOPS_COST) {
|
||||
return callback(new VerifyError(block,
|
||||
'invalid',
|
||||
'bad-blk-sigops',
|
||||
100));
|
||||
}
|
||||
|
||||
if (!tx.checkInputs(height, ret)) {
|
||||
return callback(new VerifyError(block,
|
||||
'invalid',
|
||||
|
||||
@ -550,6 +550,8 @@ Mempool.prototype.addTX = function addTX(tx, callback, force) {
|
||||
return callback(err);
|
||||
|
||||
if (!tx.hasCoins()) {
|
||||
// if (tx.getSize() > 5000)
|
||||
// return callback();
|
||||
if (self.totalSize > constants.mempool.MAX_MEMPOOL_SIZE) {
|
||||
return callback(new VerifyError(tx,
|
||||
'insufficientfee',
|
||||
@ -700,7 +702,7 @@ Mempool.prototype.verify = function verify(tx, callback) {
|
||||
0));
|
||||
}
|
||||
|
||||
if (tx.getSigops(true) > constants.tx.MAX_SIGOPS) {
|
||||
if (tx.getSigopsCost(flags) > constants.tx.MAX_SIGOPS_COST) {
|
||||
return callback(new VerifyError(tx,
|
||||
'nonstandard',
|
||||
'bad-txns-too-many-sigops',
|
||||
|
||||
@ -460,15 +460,14 @@ MinerBlock.prototype.updateMerkle = function updateMerkle() {
|
||||
*/
|
||||
|
||||
MinerBlock.prototype.addTX = function addTX(tx) {
|
||||
var size;
|
||||
var cost;
|
||||
|
||||
if (tx.mutable)
|
||||
tx = tx.toTX();
|
||||
|
||||
size = this.block.getVirtualSize(true) + tx.getVirtualSize();
|
||||
cost = this.block.getCost(true) + tx.getCost();
|
||||
|
||||
// Deliver me from the block size debate, please
|
||||
if (size > constants.block.MAX_SIZE)
|
||||
if (cost > constants.block.MAX_COST)
|
||||
return false;
|
||||
|
||||
if (this.block.hasTX(tx))
|
||||
|
||||
@ -369,6 +369,14 @@ exports.hashType = {
|
||||
|
||||
exports.hashTypeByVal = utils.revMap(exports.hashType);
|
||||
|
||||
/**
|
||||
* Amount to multiply base/non-witness sizes by.
|
||||
* @const {Number}
|
||||
* @default
|
||||
*/
|
||||
|
||||
exports.WITNESS_SCALE_FACTOR = 4;
|
||||
|
||||
/**
|
||||
* Block-related constants.
|
||||
* @enum {Number}
|
||||
@ -377,7 +385,9 @@ exports.hashTypeByVal = utils.revMap(exports.hashType);
|
||||
|
||||
exports.block = {
|
||||
MAX_SIZE: 1000000,
|
||||
MAX_COST: 4000000,
|
||||
MAX_SIGOPS: 1000000 / 50,
|
||||
MAX_SIGOPS_COST: 80000,
|
||||
MEDIAN_TIMESPAN: 11,
|
||||
BIP16_TIME: 1333238400,
|
||||
SIGHASH_LIMIT: 1300000000
|
||||
@ -404,10 +414,12 @@ exports.bip30 = {
|
||||
exports.tx = {
|
||||
MAX_VERSION: 2,
|
||||
MAX_SIZE: 100000,
|
||||
MAX_COST: 400000,
|
||||
MIN_FEE: 10000,
|
||||
BARE_MULTISIG: true,
|
||||
FREE_THRESHOLD: exports.COIN.muln(144).divn(250),
|
||||
MAX_SIGOPS: exports.block.MAX_SIGOPS / 5,
|
||||
MAX_SIGOPS_COST: exports.block.MAX_SIGOPS_COST / 5,
|
||||
COINBASE_MATURITY: 100
|
||||
};
|
||||
|
||||
|
||||
@ -1423,9 +1423,8 @@ Framer.tx.size = function txSize(tx) {
|
||||
*/
|
||||
|
||||
Framer.block.virtualSize = function blockVirtualSize(block) {
|
||||
var sizes = Framer.block.sizes(block);
|
||||
var base = sizes.size - sizes.witnessSize;
|
||||
return (base * 4 + sizes.witnessSize + 3) / 4 | 0;
|
||||
var scale = constants.WITNESS_SCALE_FACTOR;
|
||||
return (Framer.block.cost(block) + scale - 1) / scale | 0;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1435,9 +1434,34 @@ Framer.block.virtualSize = function blockVirtualSize(block) {
|
||||
*/
|
||||
|
||||
Framer.tx.virtualSize = function txVirtualSize(tx) {
|
||||
var scale = constants.WITNESS_SCALE_FACTOR;
|
||||
return (Framer.tx.cost(tx) + scale - 1) / scale | 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate block cost.
|
||||
* @param {NakedBlock|Block} block
|
||||
* @returns {Number} cost
|
||||
*/
|
||||
|
||||
Framer.block.cost = function blockCost(block) {
|
||||
var sizes = Framer.block.sizes(block);
|
||||
var base = sizes.size - sizes.witnessSize;
|
||||
var scale = constants.WITNESS_SCALE_FACTOR;
|
||||
return base * (scale - 1) + sizes.size;
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate transaction cost.
|
||||
* @param {NakedTX|TX} tx
|
||||
* @returns {Number} cost
|
||||
*/
|
||||
|
||||
Framer.tx.cost = function txCost(tx) {
|
||||
var sizes = Framer.tx.sizes(tx);
|
||||
var base = sizes.size - sizes.witnessSize;
|
||||
return (base * 4 + sizes.witnessSize + 3) / 4 | 0;
|
||||
var scale = constants.WITNESS_SCALE_FACTOR;
|
||||
return base * (scale - 1) + sizes.size;
|
||||
};
|
||||
|
||||
return Framer;
|
||||
|
||||
@ -3437,9 +3437,6 @@ 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];
|
||||
|
||||
|
||||
@ -290,6 +290,17 @@ TX.prototype.getRaw = function getRaw() {
|
||||
*/
|
||||
|
||||
TX.prototype.getVirtualSize = function getVirtualSize() {
|
||||
var scale = constants.WITNESS_SCALE_FACTOR;
|
||||
return (this.getCost() + scale - 1) / scale | 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate the cost of the transaction.
|
||||
* Note that this is cached.
|
||||
* @returns {Number} cost
|
||||
*/
|
||||
|
||||
TX.prototype.getCost = function getCost() {
|
||||
var size, witnessSize, base;
|
||||
|
||||
this.getRaw();
|
||||
@ -298,7 +309,7 @@ TX.prototype.getVirtualSize = function getVirtualSize() {
|
||||
witnessSize = this._witnessSize;
|
||||
base = size - witnessSize;
|
||||
|
||||
return (base * 4 + witnessSize + 3) / 4 | 0;
|
||||
return base * (constants.WITNESS_SCALE_FACTOR - 1) + size;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -886,11 +897,11 @@ TX.prototype.getLegacySigops = function getLegacySigops() {
|
||||
var total = 0;
|
||||
var i;
|
||||
|
||||
for (i = 0; i < tx.inputs.length; i++)
|
||||
total += tx.inputs[i].script.getSigops(false);
|
||||
for (i = 0; i < this.inputs.length; i++)
|
||||
total += this.inputs[i].script.getSigops(false);
|
||||
|
||||
for (i = 0; i < tx.outputs.length; i++)
|
||||
total += tx.outputs[i].script.getSigops(false);
|
||||
for (i = 0; i < this.outputs.length; i++)
|
||||
total += this.outputs[i].script.getSigops(false);
|
||||
|
||||
return total;
|
||||
};
|
||||
@ -907,8 +918,12 @@ TX.prototype.getScripthashSigops = function getScripthashSigops() {
|
||||
if (this.isCoinbase())
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < tx.inputs.length; i++) {
|
||||
input = tx.inputs[i];
|
||||
for (i = 0; i < this.inputs.length; i++) {
|
||||
input = this.inputs[i];
|
||||
|
||||
if (!input.coin)
|
||||
continue;
|
||||
|
||||
if (input.coin.script.isScripthash())
|
||||
total += input.coin.script.getSigops(input.script);
|
||||
}
|
||||
@ -923,7 +938,7 @@ TX.prototype.getScripthashSigops = function getScripthashSigops() {
|
||||
*/
|
||||
|
||||
TX.prototype.getSigopsCost = function getSigopsCost(flags) {
|
||||
var cost = this.getLegacySigops() * 4;
|
||||
var cost = this.getLegacySigops() * constants.WITNESS_SCALE_FACTOR;
|
||||
var input, i;
|
||||
|
||||
if (flags == null)
|
||||
@ -933,10 +948,14 @@ TX.prototype.getSigopsCost = function getSigopsCost(flags) {
|
||||
return cost;
|
||||
|
||||
if (flags & constants.flags.VERIFY_P2SH)
|
||||
cost += this.getScripthashSigops() * 4;
|
||||
cost += this.getScripthashSigops() * constants.WITNESS_SCALE_FACTOR;
|
||||
|
||||
for (i = 0; i < this.inputs.length; i++) {
|
||||
input = this.inputs[i];
|
||||
|
||||
if (!input.coin)
|
||||
continue;
|
||||
|
||||
cost += Script.getWitnessSigops(
|
||||
input.script,
|
||||
input.coin.script,
|
||||
@ -989,7 +1008,7 @@ TX.prototype.isSane = function isSane(ret) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.getVirtualSize() > constants.block.MAX_SIZE) {
|
||||
if (this.getSize() > constants.block.MAX_SIZE) {
|
||||
ret.reason = 'bad-txns-oversize';
|
||||
ret.score = 100;
|
||||
return false;
|
||||
@ -1079,7 +1098,7 @@ TX.prototype.isStandard = function isStandard(flags, ret) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.getVirtualSize() > constants.tx.MAX_SIZE) {
|
||||
if (this.getCost() > constants.tx.MAX_COST) {
|
||||
ret.reason = 'tx-size';
|
||||
return false;
|
||||
}
|
||||
@ -1162,11 +1181,7 @@ TX.prototype.hasStandardInputs = function hasStandardInputs(flags) {
|
||||
|
||||
if ((flags & constants.flags.VERIFY_P2SH)
|
||||
&& input.coin.script.isScripthash()) {
|
||||
// Not accurate:
|
||||
// Failsafe to avoid getting dos'd in case we ever
|
||||
// call hasStandardInputs before isStandard.
|
||||
if (!input.script.isPushOnly())
|
||||
return false;
|
||||
assert(input.script.isPushOnly());
|
||||
|
||||
stack = new Stack();
|
||||
|
||||
|
||||
@ -7,6 +7,23 @@
|
||||
|
||||
var url = require('url');
|
||||
var querystring = require('querystring');
|
||||
var utils = require('./utils');
|
||||
|
||||
/**
|
||||
* @typedef {Object} ParsedURI
|
||||
* @property {Base58Address} address
|
||||
* @property {BN?} amount? - Amount in satoshis.
|
||||
* @property {String?} label
|
||||
* @property {String?} message
|
||||
* @property {String?} request - Payment request URL.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Parse a bitcoin URI.
|
||||
* @param {String} uri - Bitcoin URI.
|
||||
* @returns {ParsedURI}
|
||||
* @throws on non-bitcoin uri
|
||||
*/
|
||||
|
||||
exports.parse = function parse(uri) {
|
||||
var data = url.parse(uri);
|
||||
@ -24,6 +41,12 @@ exports.parse = function parse(uri) {
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether an object is a bitcoin URI.
|
||||
* @param {String} uri
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
exports.validate = function validate(uri) {
|
||||
try {
|
||||
exports.parse(uri);
|
||||
@ -33,6 +56,14 @@ exports.validate = function validate(uri) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Encode data as a bitcoin URI.
|
||||
* @param {ParsedURI|Base58Address} data/address
|
||||
* @param {BN?} amount
|
||||
* @returns {String} URI
|
||||
* @throws when no address provided
|
||||
*/
|
||||
|
||||
exports.stringify = function stringify(address, amount) {
|
||||
var query = {};
|
||||
var data = address;
|
||||
@ -57,5 +88,10 @@ exports.stringify = function stringify(address, amount) {
|
||||
if (data.request)
|
||||
query.r = data.request;
|
||||
|
||||
return uri + querystring.stringify(query);
|
||||
query = querystring.stringify(query);
|
||||
|
||||
if (query.length === 0)
|
||||
return uri;
|
||||
|
||||
return uri + '?' + query;
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user