fee work.

This commit is contained in:
Christopher Jeffrey 2016-05-13 17:13:23 -07:00
parent 6a45d693ee
commit 6d55077818
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
8 changed files with 166 additions and 40 deletions

View File

@ -95,8 +95,8 @@ function Mempool(options) {
this.minFeeRate = 0;
this.blockSinceBump = false;
this.lastFeeUpdate = utils.now();
this.minReasonableFee = constants.tx.MIN_FEE;
this.minRelayFee = constants.tx.MIN_FEE;
this.minReasonableFee = constants.tx.MIN_RELAY;
this.minRelayFee = constants.tx.MIN_RELAY;
this._init();
}
@ -911,7 +911,7 @@ Mempool.prototype.verify = function verify(entry, callback) {
var mandatory = constants.flags.MANDATORY_VERIFY_FLAGS;
var tx = entry.tx;
var ret = {};
var fee, modFee, now, size, rejectFee, minRelayFee;
var fee, modFee, now, size, rejectFee, minRelayFee, minRate;
if (this.chain.segwitActive)
mandatory |= constants.flags.VERIFY_WITNESS;
@ -944,7 +944,12 @@ Mempool.prototype.verify = function verify(entry, callback) {
fee = tx.getFee();
modFee = entry.fees;
size = entry.size;
rejectFee = tx.getMinFee(size, self.getMinRate());
minRate = self.getMinRate();
if (minRate > self.minRelayFee)
self.network.updateMinRelay(minRate);
rejectFee = tx.getMinFee(size, minRate);
minRelayFee = tx.getMinFee(size, self.minRelayFee);
if (rejectFee.cmpn(0) > 0 && modFee.cmp(rejectFee) < 0) {

View File

@ -1055,7 +1055,7 @@ MTX.prototype.selectCoins = function selectCoins(coins, options) {
var index = 0;
var tx = this.clone();
var outputValue = tx.getOutputValue();
var tryFree, i, size, change, fee, minValue;
var tryFree, i, size, change, fee, min, output;
if (!options)
options = {};
@ -1145,7 +1145,7 @@ MTX.prototype.selectCoins = function selectCoins(coins, options) {
// Calculate max possible size after signing.
size = tx.maxSize(options, true);
if (tryFree && options.height != null) {
if (tryFree && options.height > 0) {
// Note that this will only work
// if the mempool's rolling reject
// fee is zero (i.e. the mempool is
@ -1175,26 +1175,33 @@ MTX.prototype.selectCoins = function selectCoins(coins, options) {
throw err;
}
if (fee.cmp(constants.tx.MAX_FEE) > 0)
fee = constants.tx.MAX_FEE.clone();
// How much money is left after filling outputs.
change = tx.getInputValue().sub(total());
// Attempt to subtract fee.
if (options.subtractFee != null) {
minValue = fee.addn(constants.tx.DUST_THRESHOLD);
if (typeof options.subtractFee === 'number') {
i = options.subtractFee;
output = tx.outputs[i];
if (!tx.outputs[i])
if (!output)
throw new Error('Subtraction index does not exist.');
if (tx.outputs[i].value.cmp(minValue) < 0)
min = fee.add(output.getDustThreshold());
if (output.value.cmp(min) < 0)
throw new Error('Could not subtract fee.');
tx.outputs[i].value.isub(fee);
output.value.isub(fee);
} else {
for (i = 0; i < tx.outputs.length; i++) {
if (tx.outputs[i].value.cmp(minValue) >= 0) {
tx.outputs[i].value.isub(fee);
output = tx.outputs[i];
min = fee.add(output.getDustThreshold());
if (output.value.cmp(min) >= 0) {
output.value.isub(fee);
break;
}
}
@ -1220,7 +1227,7 @@ MTX.prototype.selectCoins = function selectCoins(coins, options) {
*/
MTX.prototype.fill = function fill(coins, options) {
var result, i;
var result, i, change;
assert(this.ts === 0, 'Cannot modify a confirmed tx.');
assert(this.inputs.length === 0, 'TX is already filled.');
@ -1234,16 +1241,20 @@ MTX.prototype.fill = function fill(coins, options) {
for (i = 0; i < result.coins.length; i++)
this.addInput(result.coins[i]);
if (result.change.cmpn(constants.tx.DUST_THRESHOLD) < 0) {
// Add a change output.
this.addOutput({
address: options.changeAddress,
value: result.change
});
change = this.outputs[this.outputs.length - 1];
if (change.isDust(constants.tx.MIN_RELAY)) {
// Do nothing. Change is added to fee.
assert(this.getFee().cmp(result.fee.add(result.change)) === 0);
this.outputs.pop();
this.changeIndex = -1;
assert(this.getFee().cmp(result.fee.add(result.change)) === 0);
} else {
// Add a change output.
this.addOutput({
address: options.changeAddress,
value: result.change
});
this.changeIndex = this.outputs.length - 1;
assert(this.getFee().cmp(result.fee) === 0);
}

View File

@ -30,10 +30,16 @@ var network = require('./protocol/network');
function Network(options) {
var i, keys, key, value;
if (!(this instanceof Network))
return new Network(options);
if (typeof options === 'string')
options = network[options];
assert(options, 'Network requires a type or options.');
assert(options, 'Unknown network.');
if (Network[options.type])
return Network[options.type];
keys = Object.keys(options);
@ -68,7 +74,25 @@ Network.prototype.updateHeight = function updateHeight(height) {
*/
Network.prototype.updateRate = function updateRate(rate) {
this.rate = rate;
this.feeRate = rate;
};
Network.prototype.updateMinRelay = function updateMinRelay(rate) {
this.minRelay = rate;
};
Network.prototype.getMinRelay = function getMinRelay() {
if (this.height === -1)
return this.minRate;
return Math.min(this.minRelay, this.maxRate);
};
Network.prototype.getRate = function getRate() {
if (this.height === -1)
return this.maxRate;
return Math.min(this.feeRate, this.maxRate);
};
/**
@ -78,14 +102,9 @@ Network.prototype.updateRate = function updateRate(rate) {
*/
Network.set = function set(type) {
assert(type, 'Bad network.');
if (!Network[type])
Network[type] = new Network(type);
assert(typeof type === 'string', 'Bad network.');
Network.primary = type;
return Network[type];
return Network(network[type]);
};
Network.get = function get(options) {
@ -97,16 +116,14 @@ Network.get = function get(options) {
if (options instanceof Network)
return options;
if (typeof options === 'string') {
assert(Network[options], 'Network not created.');
return Network[options];
}
if (typeof options === 'string')
return Network(network[options]);
assert(false, 'Unknown network.');
};
Network.prototype.inspect = function inspect() {
return this.type;
return '<Network: ' + this.type + '>';
};
module.exports = Network;

View File

@ -9,6 +9,7 @@ var bcoin = require('./env');
var bn = require('bn.js');
var utils = require('./utils');
var assert = utils.assert;
var BufferWriter = require('./writer');
/**
* Represents a transaction output.
@ -151,6 +152,26 @@ Output.prototype.toJSON = function toJSON() {
};
};
Output.prototype.getDustThreshold = function getDustThreshold(rate) {
var framer = bcoin.protocol.framer;
var size;
if (rate == null)
rate = constants.tx.MIN_RELAY;
if (this.script.isUnspendable())
return new bn(0);
size = framer.output(this, new BufferWriter()).written;
size += 148;
return bcoin.tx.getMinFee(size, rate).muln(3);
};
Output.prototype.isDust = function isDust(rate) {
return this.value.cmp(this.getDustThreshold(rate)) < 0;
};
/**
* Handle a deserialized JSON output object.
* @returns {NakedOutput} A "naked" output (a

View File

@ -410,6 +410,8 @@ exports.tx = {
MAX_SIZE: 100000,
MAX_COST: 400000,
MIN_FEE: 10000,
MAX_FEE: exports.COIN.divn(10),
MIN_RELAY: 10000,
BARE_MULTISIG: true,
FREE_THRESHOLD: exports.COIN.muln(144).divn(250),
MAX_SIGOPS: exports.block.MAX_SIGOPS / 5,
@ -417,7 +419,7 @@ exports.tx = {
COINBASE_MATURITY: 100
};
exports.tx.DUST_THRESHOLD = 182 * exports.tx.MIN_FEE / 1000 * 3;
exports.tx.DUST_THRESHOLD = 182 * exports.tx.MIN_RELAY / 1000 * 3;
/**
* Script-related constants.

View File

@ -414,6 +414,38 @@ main.requireStandard = true;
main.rpcPort = 8332;
/**
* Default min relay rate (the rate for mempoolRejectFee).
* @const {Number}
* @default
*/
main.minRelay = 10000;
/**
* Default normal relay rate.
* @const {Number}
* @default
*/
main.feeRate = 40000;
/**
* Default min rate.
* @const {Number}
* @default
*/
main.minRate = 10000;
/**
* Default max rate.
* @const {Number}
* @default
*/
main.maxRate = 40000;
/*
* Testnet (v3)
* https://en.bitcoin.it/wiki/Testnet
@ -552,6 +584,14 @@ testnet.requireStandard = false;
testnet.rpcPort = 18332;
testnet.minRelay = 10000;
testnet.feeRate = 20000;
testnet.minRate = 10000;
testnet.maxRate = 40000;
/*
* Regtest
*/
@ -683,6 +723,14 @@ regtest.requireStandard = false;
regtest.rpcPort = 18332;
regtest.minRelay = 10000;
regtest.feeRate = 20000;
regtest.minRate = 10000;
regtest.maxRate = 40000;
/*
* segnet3
*/
@ -797,6 +845,14 @@ segnet3.requireStandard = false;
segnet3.rpcPort = 28332;
segnet3.minRelay = 10000;
segnet3.feeRate = 20000;
segnet3.minRate = 10000;
segnet3.maxRate = 40000;
/*
* segnet4
*/
@ -924,3 +980,11 @@ segnet4.address.versionsByVal = utils.revMap(segnet4.address.versions);
segnet4.requireStandard = false;
segnet4.rpcPort = 28902;
segnet4.minRelay = 10000;
segnet4.feeRate = 20000;
segnet4.minRate = 10000;
segnet4.maxRate = 40000;

View File

@ -1203,7 +1203,7 @@ TX.prototype.isStandard = function isStandard(flags, ret) {
return false;
}
if (output.value.cmpn(constants.tx.DUST_THRESHOLD) < 0) {
if (output.isDust(constants.tx.MIN_RELAY)) {
ret.reason = 'dust';
return false;
}
@ -1464,13 +1464,17 @@ TX.prototype.isFree = function isFree(height, size) {
*/
TX.prototype.getMinFee = function getMinFee(size, rate) {
var fee;
if (size == null)
size = this.maxSize();
return TX.getMinFee(size, rate);
};
TX.getMinFee = function getMinFee(size, rate) {
var fee;
if (rate == null)
rate = constants.tx.MIN_FEE;
rate = constants.tx.MIN_RELAY;
fee = new bn(rate).muln(size).divn(1000);
@ -1497,7 +1501,7 @@ TX.prototype.getMaxFee = function getMaxFee(size, rate) {
size = this.maxSize();
if (rate == null)
rate = constants.tx.MIN_FEE;
rate = constants.tx.MIN_RELAY;
fee = new bn(rate).muln(Math.ceil(size / 1000));

View File

@ -713,6 +713,8 @@ Wallet.prototype.fill = function fill(tx, options, callback) {
fee: options.fee,
subtractFee: options.subtractFee,
changeAddress: self.changeAddress.getAddress(),
height: self.network.height,
rate: self.network.getMinRelay(),
wallet: self,
m: self.m,
n: self.n