fee work.
This commit is contained in:
parent
6a45d693ee
commit
6d55077818
@ -95,8 +95,8 @@ function Mempool(options) {
|
|||||||
this.minFeeRate = 0;
|
this.minFeeRate = 0;
|
||||||
this.blockSinceBump = false;
|
this.blockSinceBump = false;
|
||||||
this.lastFeeUpdate = utils.now();
|
this.lastFeeUpdate = utils.now();
|
||||||
this.minReasonableFee = constants.tx.MIN_FEE;
|
this.minReasonableFee = constants.tx.MIN_RELAY;
|
||||||
this.minRelayFee = constants.tx.MIN_FEE;
|
this.minRelayFee = constants.tx.MIN_RELAY;
|
||||||
|
|
||||||
this._init();
|
this._init();
|
||||||
}
|
}
|
||||||
@ -911,7 +911,7 @@ Mempool.prototype.verify = function verify(entry, callback) {
|
|||||||
var mandatory = constants.flags.MANDATORY_VERIFY_FLAGS;
|
var mandatory = constants.flags.MANDATORY_VERIFY_FLAGS;
|
||||||
var tx = entry.tx;
|
var tx = entry.tx;
|
||||||
var ret = {};
|
var ret = {};
|
||||||
var fee, modFee, now, size, rejectFee, minRelayFee;
|
var fee, modFee, now, size, rejectFee, minRelayFee, minRate;
|
||||||
|
|
||||||
if (this.chain.segwitActive)
|
if (this.chain.segwitActive)
|
||||||
mandatory |= constants.flags.VERIFY_WITNESS;
|
mandatory |= constants.flags.VERIFY_WITNESS;
|
||||||
@ -944,7 +944,12 @@ Mempool.prototype.verify = function verify(entry, callback) {
|
|||||||
fee = tx.getFee();
|
fee = tx.getFee();
|
||||||
modFee = entry.fees;
|
modFee = entry.fees;
|
||||||
size = entry.size;
|
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);
|
minRelayFee = tx.getMinFee(size, self.minRelayFee);
|
||||||
|
|
||||||
if (rejectFee.cmpn(0) > 0 && modFee.cmp(rejectFee) < 0) {
|
if (rejectFee.cmpn(0) > 0 && modFee.cmp(rejectFee) < 0) {
|
||||||
|
|||||||
@ -1055,7 +1055,7 @@ MTX.prototype.selectCoins = function selectCoins(coins, options) {
|
|||||||
var index = 0;
|
var index = 0;
|
||||||
var tx = this.clone();
|
var tx = this.clone();
|
||||||
var outputValue = tx.getOutputValue();
|
var outputValue = tx.getOutputValue();
|
||||||
var tryFree, i, size, change, fee, minValue;
|
var tryFree, i, size, change, fee, min, output;
|
||||||
|
|
||||||
if (!options)
|
if (!options)
|
||||||
options = {};
|
options = {};
|
||||||
@ -1145,7 +1145,7 @@ MTX.prototype.selectCoins = function selectCoins(coins, options) {
|
|||||||
// Calculate max possible size after signing.
|
// Calculate max possible size after signing.
|
||||||
size = tx.maxSize(options, true);
|
size = tx.maxSize(options, true);
|
||||||
|
|
||||||
if (tryFree && options.height != null) {
|
if (tryFree && options.height > 0) {
|
||||||
// Note that this will only work
|
// Note that this will only work
|
||||||
// if the mempool's rolling reject
|
// if the mempool's rolling reject
|
||||||
// fee is zero (i.e. the mempool is
|
// fee is zero (i.e. the mempool is
|
||||||
@ -1175,26 +1175,33 @@ MTX.prototype.selectCoins = function selectCoins(coins, options) {
|
|||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fee.cmp(constants.tx.MAX_FEE) > 0)
|
||||||
|
fee = constants.tx.MAX_FEE.clone();
|
||||||
|
|
||||||
// How much money is left after filling outputs.
|
// How much money is left after filling outputs.
|
||||||
change = tx.getInputValue().sub(total());
|
change = tx.getInputValue().sub(total());
|
||||||
|
|
||||||
// Attempt to subtract fee.
|
// Attempt to subtract fee.
|
||||||
if (options.subtractFee != null) {
|
if (options.subtractFee != null) {
|
||||||
minValue = fee.addn(constants.tx.DUST_THRESHOLD);
|
|
||||||
if (typeof options.subtractFee === 'number') {
|
if (typeof options.subtractFee === 'number') {
|
||||||
i = options.subtractFee;
|
i = options.subtractFee;
|
||||||
|
output = tx.outputs[i];
|
||||||
|
|
||||||
if (!tx.outputs[i])
|
if (!output)
|
||||||
throw new Error('Subtraction index does not exist.');
|
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.');
|
throw new Error('Could not subtract fee.');
|
||||||
|
|
||||||
tx.outputs[i].value.isub(fee);
|
output.value.isub(fee);
|
||||||
} else {
|
} else {
|
||||||
for (i = 0; i < tx.outputs.length; i++) {
|
for (i = 0; i < tx.outputs.length; i++) {
|
||||||
if (tx.outputs[i].value.cmp(minValue) >= 0) {
|
output = tx.outputs[i];
|
||||||
tx.outputs[i].value.isub(fee);
|
min = fee.add(output.getDustThreshold());
|
||||||
|
if (output.value.cmp(min) >= 0) {
|
||||||
|
output.value.isub(fee);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1220,7 +1227,7 @@ MTX.prototype.selectCoins = function selectCoins(coins, options) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
MTX.prototype.fill = function fill(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.ts === 0, 'Cannot modify a confirmed tx.');
|
||||||
assert(this.inputs.length === 0, 'TX is already filled.');
|
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++)
|
for (i = 0; i < result.coins.length; i++)
|
||||||
this.addInput(result.coins[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.
|
// Do nothing. Change is added to fee.
|
||||||
assert(this.getFee().cmp(result.fee.add(result.change)) === 0);
|
this.outputs.pop();
|
||||||
this.changeIndex = -1;
|
this.changeIndex = -1;
|
||||||
|
assert(this.getFee().cmp(result.fee.add(result.change)) === 0);
|
||||||
} else {
|
} else {
|
||||||
// Add a change output.
|
|
||||||
this.addOutput({
|
|
||||||
address: options.changeAddress,
|
|
||||||
value: result.change
|
|
||||||
});
|
|
||||||
this.changeIndex = this.outputs.length - 1;
|
this.changeIndex = this.outputs.length - 1;
|
||||||
assert(this.getFee().cmp(result.fee) === 0);
|
assert(this.getFee().cmp(result.fee) === 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,10 +30,16 @@ var network = require('./protocol/network');
|
|||||||
function Network(options) {
|
function Network(options) {
|
||||||
var i, keys, key, value;
|
var i, keys, key, value;
|
||||||
|
|
||||||
|
if (!(this instanceof Network))
|
||||||
|
return new Network(options);
|
||||||
|
|
||||||
if (typeof options === 'string')
|
if (typeof options === 'string')
|
||||||
options = network[options];
|
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);
|
keys = Object.keys(options);
|
||||||
|
|
||||||
@ -68,7 +74,25 @@ Network.prototype.updateHeight = function updateHeight(height) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Network.prototype.updateRate = function updateRate(rate) {
|
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) {
|
Network.set = function set(type) {
|
||||||
assert(type, 'Bad network.');
|
assert(typeof type === 'string', 'Bad network.');
|
||||||
|
|
||||||
if (!Network[type])
|
|
||||||
Network[type] = new Network(type);
|
|
||||||
|
|
||||||
Network.primary = type;
|
Network.primary = type;
|
||||||
|
return Network(network[type]);
|
||||||
return Network[type];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Network.get = function get(options) {
|
Network.get = function get(options) {
|
||||||
@ -97,16 +116,14 @@ Network.get = function get(options) {
|
|||||||
if (options instanceof Network)
|
if (options instanceof Network)
|
||||||
return options;
|
return options;
|
||||||
|
|
||||||
if (typeof options === 'string') {
|
if (typeof options === 'string')
|
||||||
assert(Network[options], 'Network not created.');
|
return Network(network[options]);
|
||||||
return Network[options];
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(false, 'Unknown network.');
|
assert(false, 'Unknown network.');
|
||||||
};
|
};
|
||||||
|
|
||||||
Network.prototype.inspect = function inspect() {
|
Network.prototype.inspect = function inspect() {
|
||||||
return this.type;
|
return '<Network: ' + this.type + '>';
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = Network;
|
module.exports = Network;
|
||||||
|
|||||||
@ -9,6 +9,7 @@ var bcoin = require('./env');
|
|||||||
var bn = require('bn.js');
|
var bn = require('bn.js');
|
||||||
var utils = require('./utils');
|
var utils = require('./utils');
|
||||||
var assert = utils.assert;
|
var assert = utils.assert;
|
||||||
|
var BufferWriter = require('./writer');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a transaction output.
|
* 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.
|
* Handle a deserialized JSON output object.
|
||||||
* @returns {NakedOutput} A "naked" output (a
|
* @returns {NakedOutput} A "naked" output (a
|
||||||
|
|||||||
@ -410,6 +410,8 @@ exports.tx = {
|
|||||||
MAX_SIZE: 100000,
|
MAX_SIZE: 100000,
|
||||||
MAX_COST: 400000,
|
MAX_COST: 400000,
|
||||||
MIN_FEE: 10000,
|
MIN_FEE: 10000,
|
||||||
|
MAX_FEE: exports.COIN.divn(10),
|
||||||
|
MIN_RELAY: 10000,
|
||||||
BARE_MULTISIG: true,
|
BARE_MULTISIG: true,
|
||||||
FREE_THRESHOLD: exports.COIN.muln(144).divn(250),
|
FREE_THRESHOLD: exports.COIN.muln(144).divn(250),
|
||||||
MAX_SIGOPS: exports.block.MAX_SIGOPS / 5,
|
MAX_SIGOPS: exports.block.MAX_SIGOPS / 5,
|
||||||
@ -417,7 +419,7 @@ exports.tx = {
|
|||||||
COINBASE_MATURITY: 100
|
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.
|
* Script-related constants.
|
||||||
|
|||||||
@ -414,6 +414,38 @@ main.requireStandard = true;
|
|||||||
|
|
||||||
main.rpcPort = 8332;
|
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)
|
* Testnet (v3)
|
||||||
* https://en.bitcoin.it/wiki/Testnet
|
* https://en.bitcoin.it/wiki/Testnet
|
||||||
@ -552,6 +584,14 @@ testnet.requireStandard = false;
|
|||||||
|
|
||||||
testnet.rpcPort = 18332;
|
testnet.rpcPort = 18332;
|
||||||
|
|
||||||
|
testnet.minRelay = 10000;
|
||||||
|
|
||||||
|
testnet.feeRate = 20000;
|
||||||
|
|
||||||
|
testnet.minRate = 10000;
|
||||||
|
|
||||||
|
testnet.maxRate = 40000;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Regtest
|
* Regtest
|
||||||
*/
|
*/
|
||||||
@ -683,6 +723,14 @@ regtest.requireStandard = false;
|
|||||||
|
|
||||||
regtest.rpcPort = 18332;
|
regtest.rpcPort = 18332;
|
||||||
|
|
||||||
|
regtest.minRelay = 10000;
|
||||||
|
|
||||||
|
regtest.feeRate = 20000;
|
||||||
|
|
||||||
|
regtest.minRate = 10000;
|
||||||
|
|
||||||
|
regtest.maxRate = 40000;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* segnet3
|
* segnet3
|
||||||
*/
|
*/
|
||||||
@ -797,6 +845,14 @@ segnet3.requireStandard = false;
|
|||||||
|
|
||||||
segnet3.rpcPort = 28332;
|
segnet3.rpcPort = 28332;
|
||||||
|
|
||||||
|
segnet3.minRelay = 10000;
|
||||||
|
|
||||||
|
segnet3.feeRate = 20000;
|
||||||
|
|
||||||
|
segnet3.minRate = 10000;
|
||||||
|
|
||||||
|
segnet3.maxRate = 40000;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* segnet4
|
* segnet4
|
||||||
*/
|
*/
|
||||||
@ -924,3 +980,11 @@ segnet4.address.versionsByVal = utils.revMap(segnet4.address.versions);
|
|||||||
segnet4.requireStandard = false;
|
segnet4.requireStandard = false;
|
||||||
|
|
||||||
segnet4.rpcPort = 28902;
|
segnet4.rpcPort = 28902;
|
||||||
|
|
||||||
|
segnet4.minRelay = 10000;
|
||||||
|
|
||||||
|
segnet4.feeRate = 20000;
|
||||||
|
|
||||||
|
segnet4.minRate = 10000;
|
||||||
|
|
||||||
|
segnet4.maxRate = 40000;
|
||||||
|
|||||||
@ -1203,7 +1203,7 @@ TX.prototype.isStandard = function isStandard(flags, ret) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (output.value.cmpn(constants.tx.DUST_THRESHOLD) < 0) {
|
if (output.isDust(constants.tx.MIN_RELAY)) {
|
||||||
ret.reason = 'dust';
|
ret.reason = 'dust';
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1464,13 +1464,17 @@ TX.prototype.isFree = function isFree(height, size) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
TX.prototype.getMinFee = function getMinFee(size, rate) {
|
TX.prototype.getMinFee = function getMinFee(size, rate) {
|
||||||
var fee;
|
|
||||||
|
|
||||||
if (size == null)
|
if (size == null)
|
||||||
size = this.maxSize();
|
size = this.maxSize();
|
||||||
|
|
||||||
|
return TX.getMinFee(size, rate);
|
||||||
|
};
|
||||||
|
|
||||||
|
TX.getMinFee = function getMinFee(size, rate) {
|
||||||
|
var fee;
|
||||||
|
|
||||||
if (rate == null)
|
if (rate == null)
|
||||||
rate = constants.tx.MIN_FEE;
|
rate = constants.tx.MIN_RELAY;
|
||||||
|
|
||||||
fee = new bn(rate).muln(size).divn(1000);
|
fee = new bn(rate).muln(size).divn(1000);
|
||||||
|
|
||||||
@ -1497,7 +1501,7 @@ TX.prototype.getMaxFee = function getMaxFee(size, rate) {
|
|||||||
size = this.maxSize();
|
size = this.maxSize();
|
||||||
|
|
||||||
if (rate == null)
|
if (rate == null)
|
||||||
rate = constants.tx.MIN_FEE;
|
rate = constants.tx.MIN_RELAY;
|
||||||
|
|
||||||
fee = new bn(rate).muln(Math.ceil(size / 1000));
|
fee = new bn(rate).muln(Math.ceil(size / 1000));
|
||||||
|
|
||||||
|
|||||||
@ -713,6 +713,8 @@ Wallet.prototype.fill = function fill(tx, options, callback) {
|
|||||||
fee: options.fee,
|
fee: options.fee,
|
||||||
subtractFee: options.subtractFee,
|
subtractFee: options.subtractFee,
|
||||||
changeAddress: self.changeAddress.getAddress(),
|
changeAddress: self.changeAddress.getAddress(),
|
||||||
|
height: self.network.height,
|
||||||
|
rate: self.network.getMinRelay(),
|
||||||
wallet: self,
|
wallet: self,
|
||||||
m: self.m,
|
m: self.m,
|
||||||
n: self.n
|
n: self.n
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user