improve fee calculation. tests.
This commit is contained in:
parent
72e5d5fbc7
commit
f013ddb353
@ -800,7 +800,7 @@ HDPrivateKey._generate = function _generate(options, network) {
|
||||
var privateKey, entropy;
|
||||
|
||||
if (!options)
|
||||
opitons = {};
|
||||
options = {};
|
||||
|
||||
if (Buffer.isBuffer(options))
|
||||
options = { privateKey: options };
|
||||
|
||||
@ -730,11 +730,10 @@ MTX.prototype.isScripted = function isScripted() {
|
||||
for (i = 0; i < this.inputs.length; i++) {
|
||||
input = this.inputs[i];
|
||||
|
||||
if (input.script.code.length === 0)
|
||||
return false;
|
||||
|
||||
if (input.witness.items.length === 0)
|
||||
if (input.script.code.length === 0
|
||||
&& input.witness.items.length === 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -776,7 +775,6 @@ MTX.prototype.maxSize = function maxSize(options, force) {
|
||||
input = this.inputs[i];
|
||||
size = input.script.getSize();
|
||||
total -= utils.sizeVarint(size) + size;
|
||||
total += 1;
|
||||
}
|
||||
|
||||
// Add size for signatures and public keys
|
||||
@ -784,6 +782,7 @@ MTX.prototype.maxSize = function maxSize(options, force) {
|
||||
input = this.inputs[i];
|
||||
size = 0;
|
||||
witness = false;
|
||||
redeem = null;
|
||||
|
||||
// We're out of luck here.
|
||||
// Just assume it's a p2pkh.
|
||||
@ -817,10 +816,17 @@ MTX.prototype.maxSize = function maxSize(options, force) {
|
||||
if (prev.isWitnessProgram()) {
|
||||
witness = true;
|
||||
|
||||
// Now calculating vsize. The regular
|
||||
// redeem script (if there was one)
|
||||
// is now worth 4 points.
|
||||
size *= 4;
|
||||
// Now calculating vsize.
|
||||
if (redeem) {
|
||||
// The regular redeem script
|
||||
// is now worth 4 points.
|
||||
size += utils.sizeVarint(size);
|
||||
size *= 4;
|
||||
} else {
|
||||
// Add one varint byte back
|
||||
// for the 0-byte input script.
|
||||
size += 1 * 4;
|
||||
}
|
||||
|
||||
// Add 2 bytes for flag and marker.
|
||||
if (!hadWitness)
|
||||
@ -898,14 +904,14 @@ MTX.prototype.maxSize = function maxSize(options, force) {
|
||||
}
|
||||
}
|
||||
|
||||
// Byte for varint size of input script or witness.
|
||||
size += utils.sizeVarint(size);
|
||||
|
||||
// Calculate vsize if we're a witness program.
|
||||
if (witness) {
|
||||
// Add one byte back for the 0-byte input script.
|
||||
size += 1 * 4;
|
||||
// Calculate vsize if
|
||||
// we're a witness program.
|
||||
size = (size + scale - 1) / scale | 0;
|
||||
} else {
|
||||
// Byte for varint
|
||||
// size of input script.
|
||||
size += utils.sizeVarint(size);
|
||||
}
|
||||
|
||||
total += size;
|
||||
@ -920,10 +926,10 @@ MTX.prototype.maxSize = function maxSize(options, force) {
|
||||
* @param {Object?} options
|
||||
* @param {String?} options.selection - Coin selection priority. Can
|
||||
* be `age`, `random`, or `all`. (default=age).
|
||||
* @param {Boolean} options.accurate - Whether to use "accurate"
|
||||
* fee calculatation rather than rounding to the nearest kilobyte.
|
||||
* See {@link TX#getMinFee} vs. {@link TX#getMaxFee}.
|
||||
* @param {Boolean} options.confirmed - Select only confirmed coins.
|
||||
* @param {Boolean} options.round - Whether to round to the nearest
|
||||
* kilobyte for fee calculation.
|
||||
* See {@link TX#getMinFee} vs. {@link TX#getRoundFee}.
|
||||
* @param {Boolean} options.free - Do not apply a fee if the
|
||||
* transaction priority is high enough to be considered free.
|
||||
* @param {Amount?} options.fee - Use a hard fee rather than calculating one.
|
||||
@ -1042,10 +1048,10 @@ MTX.prototype.selectCoins = function selectCoins(coins, options) {
|
||||
tryFree = false;
|
||||
}
|
||||
|
||||
if (options.accurate)
|
||||
fee = tx.getMinFee(size, options.rate);
|
||||
if (options.round)
|
||||
fee = tx.getRoundFee(size, options.rate);
|
||||
else
|
||||
fee = tx.getMaxFee(size, options.rate);
|
||||
fee = tx.getMinFee(size, options.rate);
|
||||
|
||||
// Failed to get enough funds, add more coins.
|
||||
if (!isFull())
|
||||
|
||||
@ -428,7 +428,7 @@ main.minRelay = 10000;
|
||||
* @default
|
||||
*/
|
||||
|
||||
main.feeRate = 40000;
|
||||
main.feeRate = 50000;
|
||||
|
||||
/**
|
||||
* Default min rate.
|
||||
@ -444,7 +444,7 @@ main.minRate = 10000;
|
||||
* @default
|
||||
*/
|
||||
|
||||
main.maxRate = 40000;
|
||||
main.maxRate = 50000;
|
||||
|
||||
/*
|
||||
* Testnet (v3)
|
||||
|
||||
@ -1540,7 +1540,7 @@ TX.prototype.getRate = function getRate(size) {
|
||||
* @returns {Amount} fee
|
||||
*/
|
||||
|
||||
TX.prototype.getMaxFee = function getMaxFee(size, rate) {
|
||||
TX.prototype.getRoundFee = function getRoundFee(size, rate) {
|
||||
var fee;
|
||||
|
||||
if (size == null)
|
||||
|
||||
@ -677,9 +677,9 @@ Wallet.prototype.ownOutput = function ownOutput(tx, index) {
|
||||
* @param {Object?} options
|
||||
* @param {String?} options.selection - Coin selection priority. Can
|
||||
* be `age`, `random`, or `all`. (default=age).
|
||||
* @param {Boolean} options.accurate - Whether to use "accurate"
|
||||
* fee calculatation rather than rounding to the nearest kilobyte.
|
||||
* See {@link TX#getMinFee} vs. {@link TX#getMaxFee}.
|
||||
* @param {Boolean} options.round - Whether to round to the nearest
|
||||
* kilobyte for fee calculation.
|
||||
* See {@link TX#getMinFee} vs. {@link TX#getRoundFee}.
|
||||
* @param {Rate} options.rate - Rate used for fee calculation.
|
||||
* @param {Boolean} options.confirmed - Select only confirmed coins.
|
||||
* @param {Boolean} options.free - Do not apply a fee if the
|
||||
@ -709,14 +709,16 @@ Wallet.prototype.fill = function fill(tx, options, callback) {
|
||||
try {
|
||||
tx.fill(coins, {
|
||||
selection: options.selection || 'age',
|
||||
accurate: options.accurate,
|
||||
round: options.round,
|
||||
confirmed: options.confirmed,
|
||||
free: options.free,
|
||||
fee: options.fee,
|
||||
subtractFee: options.subtractFee,
|
||||
changeAddress: self.changeAddress.getAddress(),
|
||||
height: self.network.height,
|
||||
rate: self.network.getMinRelay(),
|
||||
rate: options.rate != null
|
||||
? options.rate
|
||||
: self.network.getRate(),
|
||||
wallet: self,
|
||||
m: self.m,
|
||||
n: self.n
|
||||
|
||||
@ -100,27 +100,27 @@ describe('Mempool', function() {
|
||||
assert.ifError(err);
|
||||
mempool.getBalance(function(err, balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(balance.total.toString(10), '0');
|
||||
assert.equal(balance.total, 0);
|
||||
mempool.addTX(t1, function(err) {
|
||||
assert.ifError(err);
|
||||
mempool.getBalance(function(err, balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(balance.total.toString(10), '60000');
|
||||
assert.equal(balance.total, 60000);
|
||||
mempool.addTX(t2, function(err) {
|
||||
assert.ifError(err);
|
||||
mempool.getBalance(function(err, balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(balance.total.toString(10), '50000');
|
||||
assert.equal(balance.total, 50000);
|
||||
mempool.addTX(t3, function(err) {
|
||||
assert.ifError(err);
|
||||
mempool.getBalance(function(err, balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(balance.total.toString(10), '22000');
|
||||
assert.equal(balance.total, 22000);
|
||||
mempool.addTX(f1, function(err) {
|
||||
assert.ifError(err);
|
||||
mempool.getBalance(function(err, balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(balance.total.toString(10), '20000');
|
||||
assert.equal(balance.total, 20000);
|
||||
mempool.getHistory(function(err, txs) {
|
||||
assert(txs.some(function(tx) {
|
||||
return tx.hash('hex') === f1.hash('hex');
|
||||
|
||||
@ -5,6 +5,16 @@ var network = bcoin.protocol.network;
|
||||
var utils = bcoin.utils;
|
||||
var assert = require('assert');
|
||||
|
||||
var KEY1 = 'xprv9s21ZrQH143K3Aj6xQBymM31Zb4BVc7wxqfUhMZrzewdDVCt'
|
||||
+ 'qUP9iWfcHgJofs25xbaUpCps9GDXj83NiWvQCAkWQhVj5J4CorfnpKX94AZ';
|
||||
|
||||
KEY1 = { xprivkey: KEY1 };
|
||||
|
||||
var KEY2 = 'xprv9s21ZrQH143K3mqiSThzPtWAabQ22Pjp3uSNnZ53A5bQ4udp'
|
||||
+ 'faKekc2m4AChLYH1XDzANhrSdxHYWUeTWjYJwFwWFyHkTMnMeAcW4JyRCZa';
|
||||
|
||||
KEY2 = { xprivkey: KEY2 };
|
||||
|
||||
var dummyInput = {
|
||||
prevout: {
|
||||
hash: constants.NULL_HASH,
|
||||
@ -24,6 +34,18 @@ var dummyInput = {
|
||||
sequence: 0xffffffff
|
||||
};
|
||||
|
||||
assert.range = function range(value, lo, hi, message) {
|
||||
if (!(value >= lo && value <= hi)) {
|
||||
throw new assert.AssertionError({
|
||||
message: message,
|
||||
actual: value,
|
||||
expected: lo + ', ' + hi,
|
||||
operator: '>= && <=',
|
||||
stackStartFunction: range
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
describe('Wallet', function() {
|
||||
var wdb = new bcoin.walletdb({
|
||||
name: 'wallet-test',
|
||||
@ -199,33 +221,33 @@ describe('Wallet', function() {
|
||||
assert.ifError(err);
|
||||
w.getBalance(function(err, balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(balance.total.toString(10), '22500');
|
||||
assert.equal(balance.total, 22500);
|
||||
wdb.addTX(t1, function(err) {
|
||||
w.getBalance(function(err, balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(balance.total.toString(10), '73000');
|
||||
assert.equal(balance.total, 73000);
|
||||
wdb.addTX(t2, function(err) {
|
||||
assert.ifError(err);
|
||||
w.getBalance(function(err, balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(balance.total.toString(10), '47000');
|
||||
assert.equal(balance.total, 47000);
|
||||
wdb.addTX(t3, function(err) {
|
||||
assert.ifError(err);
|
||||
w.getBalance(function(err, balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(balance.total.toString(10), '22000');
|
||||
assert.equal(balance.total, 22000);
|
||||
wdb.addTX(f1, function(err) {
|
||||
assert.ifError(err);
|
||||
w.getBalance(function(err, balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(balance.total.toString(10), '11000');
|
||||
assert.equal(balance.total, 11000);
|
||||
w.getHistory(function(err, txs) {
|
||||
assert(txs.some(function(tx) {
|
||||
return tx.hash('hex') === f1.hash('hex');
|
||||
}));
|
||||
|
||||
var w2 = bcoin.wallet.fromJSON(w.toJSON());
|
||||
// assert.equal(w2.getBalance().toString(10), '11000');
|
||||
// assert.equal(w2.getBalance(), 11000);
|
||||
// assert(w2.getHistory().some(function(tx) {
|
||||
// return tx.hash('hex') === f1.hash('hex');
|
||||
// }));
|
||||
@ -253,7 +275,7 @@ describe('Wallet', function() {
|
||||
assert.ifError(err);
|
||||
dw.getBalance(function(err, balance) {
|
||||
assert.ifError(err);
|
||||
assert.equal(balance.total.toString(10), '11000');
|
||||
assert.equal(balance.total, 11000);
|
||||
cb();
|
||||
});
|
||||
});
|
||||
@ -280,22 +302,23 @@ describe('Wallet', function() {
|
||||
|
||||
// Create new transaction
|
||||
var t2 = bcoin.mtx().addOutput(w2, 5460);
|
||||
w1.fill(t2, function(err) {
|
||||
w1.fill(t2, { rate: 10000, round: true }, function(err) {
|
||||
assert.ifError(err);
|
||||
w1.sign(t2);
|
||||
assert(t2.verify());
|
||||
|
||||
assert.equal(t2.getInputValue().toString(10), 16380);
|
||||
assert.equal(t2.getInputValue(), 16380);
|
||||
// If change < dust and is added to outputs:
|
||||
// assert.equal(t2.getOutputValue().toString(10), 6380);
|
||||
// If change < dust and is added to fee:
|
||||
assert.equal(t2.getOutputValue().toString(10), 5460);
|
||||
// assert.equal(t2.getOutputValue(), 6380);
|
||||
// If change > dust and is added to fee:
|
||||
assert.equal(t2.getOutputValue(), 5460);
|
||||
assert.equal(t2.getFee(), 10920);
|
||||
|
||||
// Create new transaction
|
||||
var t3 = bcoin.mtx().addOutput(w2, 15000);
|
||||
w1.fill(t3, function(err) {
|
||||
w1.fill(t3, { rate: 10000, round: true }, function(err) {
|
||||
assert(err);
|
||||
assert.equal(err.requiredFunds.toString(10), 25000);
|
||||
assert.equal(err.requiredFunds, 25000);
|
||||
cb();
|
||||
});
|
||||
});
|
||||
@ -304,6 +327,59 @@ describe('Wallet', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('should fill tx with inputs with accurate fee', function(cb) {
|
||||
wdb.create({ master: KEY1 }, function(err, w1) {
|
||||
assert.ifError(err);
|
||||
wdb.create({ master: KEY2 }, function(err, w2) {
|
||||
assert.ifError(err);
|
||||
|
||||
// Coinbase
|
||||
var t1 = bcoin.mtx()
|
||||
.addOutput(w1, 5460)
|
||||
.addOutput(w1, 5460)
|
||||
.addOutput(w1, 5460)
|
||||
.addOutput(w1, 5460);
|
||||
|
||||
t1.addInput(dummyInput);
|
||||
|
||||
// Fake TX should temporarly change output
|
||||
wdb.addTX(t1, function(err) {
|
||||
assert.ifError(err);
|
||||
|
||||
// Create new transaction
|
||||
var t2 = bcoin.mtx().addOutput(w2, 5460);
|
||||
w1.fill(t2, { rate: 10000 }, function(err) {
|
||||
assert.ifError(err);
|
||||
w1.sign(t2);
|
||||
assert(t2.verify());
|
||||
|
||||
assert.equal(t2.getInputValue(), 16380);
|
||||
|
||||
// Should now have a change output:
|
||||
assert.equal(t2.getOutputValue(), 11130);
|
||||
|
||||
assert.equal(t2.getFee(), 5250);
|
||||
|
||||
assert.equal(t2.getCost(), 2084);
|
||||
assert.equal(t2.getBaseSize(), 521);
|
||||
assert.equal(t2.getSize(), 521);
|
||||
assert.equal(t2.getVirtualSize(), 521);
|
||||
|
||||
// Create new transaction
|
||||
wdb.addTX(t2, function(err) {
|
||||
assert.ifError(err);
|
||||
var t3 = bcoin.mtx().addOutput(w2, 15000);
|
||||
w1.fill(t3, { rate: 10000 }, function(err) {
|
||||
assert(err);
|
||||
cb();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should sign multiple inputs using different keys', function(cb) {
|
||||
wdb.create({}, function(err, w1) {
|
||||
assert.ifError(err);
|
||||
@ -483,7 +559,7 @@ describe('Wallet', function() {
|
||||
var send = bcoin.mtx();
|
||||
send.addOutput({ address: receive.getAddress(), value: 5460 });
|
||||
assert(!send.verify(null, true, flags));
|
||||
w1.fill(send, { m: w1.m, n: w1.n }, function(err) {
|
||||
w1.fill(send, { rate: 10000, round: true }, function(err) {
|
||||
assert.ifError(err);
|
||||
|
||||
w1.sign(send);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user