chain: optimize reward calculation.
This commit is contained in:
parent
2a69b7ab7a
commit
f71f2d954b
@ -571,13 +571,15 @@ Chain.prototype.verifyDuplicates = co(function* verifyDuplicates(block, prev, st
|
||||
*/
|
||||
|
||||
Chain.prototype.verifyInputs = co(function* verifyInputs(block, prev, state) {
|
||||
var interval = this.network.halvingInterval;
|
||||
var ret = new VerifyResult();
|
||||
var view = new CoinView();
|
||||
var height = prev.height + 1;
|
||||
var historical = prev.isHistorical();
|
||||
var sigops = 0;
|
||||
var reward = 0;
|
||||
var jobs = [];
|
||||
var i, tx, valid;
|
||||
var i, tx, valid, fee;
|
||||
|
||||
if (this.options.spv)
|
||||
return view;
|
||||
@ -628,13 +630,24 @@ Chain.prototype.verifyInputs = co(function* verifyInputs(block, prev, state) {
|
||||
|
||||
// Contextual sanity checks.
|
||||
if (i > 0) {
|
||||
if (!tx.checkInputs(view, height, ret)) {
|
||||
fee = tx.checkContext(view, height, ret);
|
||||
|
||||
if (fee === -1) {
|
||||
throw new VerifyError(block,
|
||||
'invalid',
|
||||
ret.reason,
|
||||
ret.score);
|
||||
}
|
||||
|
||||
reward += fee;
|
||||
|
||||
if (reward > consensus.MAX_MONEY) {
|
||||
throw new VerifyError(block,
|
||||
'invalid',
|
||||
'bad-cb-amount',
|
||||
100);
|
||||
}
|
||||
|
||||
// Push onto verification queue.
|
||||
jobs.push(tx.verifyAsync(view, state.flags));
|
||||
}
|
||||
@ -647,7 +660,9 @@ Chain.prototype.verifyInputs = co(function* verifyInputs(block, prev, state) {
|
||||
return view;
|
||||
|
||||
// Make sure the miner isn't trying to conjure more coins.
|
||||
if (block.getClaimed() > block.getReward(view, height, this.network)) {
|
||||
reward += consensus.getReward(height, interval);
|
||||
|
||||
if (block.getClaimed() > reward) {
|
||||
throw new VerifyError(block,
|
||||
'invalid',
|
||||
'bad-cb-amount',
|
||||
|
||||
@ -538,43 +538,6 @@ Block.prototype.getCoinbaseHeight = function getCoinbaseHeight() {
|
||||
return height;
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate the block reward.
|
||||
* @returns {Amount} reward
|
||||
*/
|
||||
|
||||
Block.prototype.getReward = function getReward(view, height, network) {
|
||||
var i, tx, reward, fee;
|
||||
|
||||
assert(typeof height === 'number');
|
||||
|
||||
network = Network.get(network);
|
||||
reward = consensus.getReward(height, network.halvingInterval);
|
||||
|
||||
for (i = 1; i < this.txs.length; i++) {
|
||||
tx = this.txs[i];
|
||||
|
||||
fee = tx.getFee(view);
|
||||
|
||||
if (fee < 0 || fee > consensus.MAX_MONEY)
|
||||
return -1;
|
||||
|
||||
reward += fee;
|
||||
|
||||
// We don't want to go above 53 bits.
|
||||
// This is to make the getClaimed check
|
||||
// fail if the miner mined an evil block.
|
||||
// Note that this check ONLY works because
|
||||
// MAX_MONEY is 51 bits. The result of
|
||||
// (51 bits + 51 bits) is _never_ greater
|
||||
// than 52 bits.
|
||||
if (reward < 0 || reward > consensus.MAX_MONEY)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return reward;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the "claimed" reward by the coinbase.
|
||||
* @returns {Amount} claimed
|
||||
|
||||
@ -71,8 +71,6 @@ function TX(options) {
|
||||
this._size = -1;
|
||||
this._witness = -1;
|
||||
|
||||
this._outputValue = -1;
|
||||
this._inputValue = -1;
|
||||
this._hashPrevouts = null;
|
||||
this._hashSequence = null;
|
||||
this._hashOutputs = null;
|
||||
@ -183,9 +181,6 @@ TX.prototype.refresh = function refresh() {
|
||||
this._size = -1;
|
||||
this._witness = -1;
|
||||
|
||||
this._outputValue = -1;
|
||||
this._inputValue = -1;
|
||||
|
||||
this._hashPrevouts = null;
|
||||
this._hashSequence = null;
|
||||
this._hashOutputs = null;
|
||||
@ -891,9 +886,6 @@ TX.prototype.getInputValue = function getInputValue(view) {
|
||||
var total = 0;
|
||||
var i, input, coin;
|
||||
|
||||
if (this._inputValue !== -1)
|
||||
return this._inputValue;
|
||||
|
||||
for (i = 0; i < this.inputs.length; i++) {
|
||||
input = this.inputs[i];
|
||||
coin = view.getOutput(input);
|
||||
@ -904,9 +896,6 @@ TX.prototype.getInputValue = function getInputValue(view) {
|
||||
total += coin.value;
|
||||
}
|
||||
|
||||
if (!this.mutable)
|
||||
this._inputValue = total;
|
||||
|
||||
return total;
|
||||
};
|
||||
|
||||
@ -919,17 +908,11 @@ TX.prototype.getOutputValue = function getOutputValue() {
|
||||
var total = 0;
|
||||
var i, output;
|
||||
|
||||
if (this._outputValue !== -1)
|
||||
return this._outputValue;
|
||||
|
||||
for (i = 0; i < this.outputs.length; i++) {
|
||||
output = this.outputs[i];
|
||||
total += output.value;
|
||||
}
|
||||
|
||||
if (!this.mutable)
|
||||
this._outputValue = total;
|
||||
|
||||
return total;
|
||||
};
|
||||
|
||||
@ -1447,9 +1430,6 @@ TX.prototype.isSane = function isSane(ret) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.mutable)
|
||||
this._outputValue = total;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
@ -1757,6 +1737,25 @@ TX.prototype.getWitnessStandard = function getWitnessStandard(view) {
|
||||
*/
|
||||
|
||||
TX.prototype.checkInputs = function checkInputs(view, height, ret) {
|
||||
return this.checkContext(view, height, ret) !== -1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Perform contextual checks to verify input, output,
|
||||
* and fee values, as well as coinbase spend maturity
|
||||
* (coinbases can only be spent 100 blocks or more
|
||||
* after they're created). Note that this function is
|
||||
* consensus critical.
|
||||
* @param {CoinView} view
|
||||
* @param {Number} height - Height at which the
|
||||
* transaction is being spent. In the mempool this is
|
||||
* the chain height plus one at the time it entered the pool.
|
||||
* @param {Object?} ret - Return object, may be
|
||||
* set with properties `reason` and `score`.
|
||||
* @returns {Amount}
|
||||
*/
|
||||
|
||||
TX.prototype.checkContext = function checkContext(view, height, ret) {
|
||||
var total = 0;
|
||||
var i, input, coins, coin, fee, value;
|
||||
|
||||
@ -1772,14 +1771,14 @@ TX.prototype.checkInputs = function checkInputs(view, height, ret) {
|
||||
if (!coins) {
|
||||
ret.reason = 'bad-txns-inputs-missingorspent';
|
||||
ret.score = 0;
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (coins.coinbase) {
|
||||
if (height - coins.height < consensus.COINBASE_MATURITY) {
|
||||
ret.reason = 'bad-txns-premature-spend-of-coinbase';
|
||||
ret.score = 0;
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1788,13 +1787,13 @@ TX.prototype.checkInputs = function checkInputs(view, height, ret) {
|
||||
if (!coin) {
|
||||
ret.reason = 'bad-txns-inputs-missingorspent';
|
||||
ret.score = 0;
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (coin.value < 0 || coin.value > consensus.MAX_MONEY) {
|
||||
ret.reason = 'bad-txns-inputvalues-outofrange';
|
||||
ret.score = 100;
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
total += coin.value;
|
||||
@ -1802,7 +1801,7 @@ TX.prototype.checkInputs = function checkInputs(view, height, ret) {
|
||||
if (total < 0 || total > consensus.MAX_MONEY) {
|
||||
ret.reason = 'bad-txns-inputvalues-outofrange';
|
||||
ret.score = 100;
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1812,7 +1811,7 @@ TX.prototype.checkInputs = function checkInputs(view, height, ret) {
|
||||
if (total < value) {
|
||||
ret.reason = 'bad-txns-in-belowout';
|
||||
ret.score = 100;
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
fee = total - value;
|
||||
@ -1820,19 +1819,16 @@ TX.prototype.checkInputs = function checkInputs(view, height, ret) {
|
||||
if (fee < 0) {
|
||||
ret.reason = 'bad-txns-fee-negative';
|
||||
ret.score = 100;
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fee > consensus.MAX_MONEY) {
|
||||
ret.reason = 'bad-txns-fee-outofrange';
|
||||
ret.score = 100;
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!this.mutable)
|
||||
this._inputValue = total;
|
||||
|
||||
return true;
|
||||
return fee;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -189,6 +189,7 @@ describe('Block', function() {
|
||||
var view = new CoinView();
|
||||
var height = block300025.height;
|
||||
var sigops = 0;
|
||||
var reward = 0;
|
||||
var i, j, tx, input, coin, flags;
|
||||
|
||||
for (i = 1; i < block300025.txs.length; i++) {
|
||||
@ -216,11 +217,14 @@ describe('Block', function() {
|
||||
assert(!tx.hasWitness());
|
||||
sigops += tx.getSigopsCost(view, flags);
|
||||
view.addTX(tx, height);
|
||||
reward += tx.getFee(view);
|
||||
}
|
||||
|
||||
reward += consensus.getReward(height, 210000);
|
||||
|
||||
assert.equal(sigops, 5280);
|
||||
assert.equal(block.getReward(view, height), 2507773345);
|
||||
assert.equal(block.getReward(view, height), block.txs[0].outputs[0].value);
|
||||
assert.equal(reward, 2507773345);
|
||||
assert.equal(reward, block.txs[0].outputs[0].value);
|
||||
});
|
||||
|
||||
it('should fail with a bad merkle root', function() {
|
||||
|
||||
@ -513,32 +513,6 @@ describe('TX', function() {
|
||||
assert.ok(!tx.checkInputs(view, 0));
|
||||
});
|
||||
|
||||
it('should fail on >51 bit fees from multiple txs', function() {
|
||||
var view = new CoinView();
|
||||
var genesis = Network.get().genesis;
|
||||
var block = new Block(genesis);
|
||||
var i, tx;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
tx = new TX({
|
||||
version: 1,
|
||||
flag: 1,
|
||||
inputs: [
|
||||
createInput(Math.floor(consensus.MAX_MONEY / 2), view)
|
||||
],
|
||||
outputs: [{
|
||||
script: [],
|
||||
value: 0
|
||||
}],
|
||||
locktime: 0
|
||||
});
|
||||
|
||||
block.txs.push(tx);
|
||||
}
|
||||
|
||||
assert.equal(block.getReward(view, 0), -1);
|
||||
});
|
||||
|
||||
it('should fail to parse >53 bit values', function() {
|
||||
var view = new CoinView();
|
||||
var tx, raw;
|
||||
@ -689,31 +663,6 @@ describe('TX', function() {
|
||||
assert.ok(tx.isSane());
|
||||
assert.ok(!tx.checkInputs(view, 0));
|
||||
});
|
||||
|
||||
it('should fail on >53 bit fees from multiple txs', function() {
|
||||
var view = new CoinView();
|
||||
var genesis = Network.get().genesis;
|
||||
var block = new Block(genesis);
|
||||
var i, tx;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
tx = new TX({
|
||||
version: 1,
|
||||
flag: 1,
|
||||
inputs: [
|
||||
createInput(MAX, view)
|
||||
],
|
||||
outputs: [{
|
||||
script: [],
|
||||
value: 0
|
||||
}],
|
||||
locktime: 0
|
||||
});
|
||||
block.txs.push(tx);
|
||||
}
|
||||
|
||||
assert.equal(block.getReward(view, 0), -1);
|
||||
});
|
||||
});
|
||||
|
||||
it('should count sigops for multisig', function() {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user