fill refactor. accurate fee calculation.

This commit is contained in:
Christopher Jeffrey 2016-02-12 03:54:02 -08:00
parent c4818c4bc8
commit 016c85f8b5
2 changed files with 54 additions and 44 deletions

View File

@ -74,11 +74,7 @@ function TX(data, block) {
}
}
this.hardFee = data.hardFee || null;
this.subtractFee = data.subtractFee || null;
this.changeAddress = data.changeAddress || null;
this.changeIndex = data.changeIndex != null ? data.changeIndex : -1;
this.total = data.total || null;
// ps = Pending Since
this.ps = this.ts === 0 ? utils.now() : 0;
@ -919,7 +915,7 @@ TX.prototype.isCoinbase = function isCoinbase() {
return this.inputs.length === 1 && +this.inputs[0].prevout.hash === 0;
};
TX.prototype.maxSize = function maxSize() {
TX.prototype.maxSize = function maxSize(maxM, maxN) {
var copy = this.clone();
var i, j, input, total, size, s, m, n;
@ -959,7 +955,7 @@ TX.prototype.maxSize = function maxSize() {
// OP_PUSHDATA0 [signature]
size += 1 + 73;
// OP_PUSHDATA0 [key]
size += 1 + 65;
size += 1 + 33;
} else if (bcoin.script.isMultisig(s)) {
// Bare Multisig
// Get the previous m value:
@ -979,9 +975,9 @@ TX.prototype.maxSize = function maxSize() {
// simply add more of the fee to the change
// output.
// m value
m = 15;
m = maxM || 15;
// n value
n = 15;
n = maxN || 15;
// OP_0
size += 1;
// OP_PUSHDATA0 [signature] ...
@ -991,7 +987,7 @@ TX.prototype.maxSize = function maxSize() {
// m value
size += 1;
// OP_PUSHDATA0 [key] ...
size += (1 + 65) * n;
size += (1 + 33) * n;
// n value
size += 1;
// OP_CHECKMULTISIG
@ -1013,7 +1009,7 @@ TX.prototype.maxSize = function maxSize() {
return total;
};
TX.prototype.getInputs = function getInputs(unspent, address, fee) {
TX.prototype.getInputs = function getInputs(unspent, options) {
var self = this;
var tx = this.clone();
var outputValue = tx.getOutputValue();
@ -1023,6 +1019,14 @@ TX.prototype.getInputs = function getInputs(unspent, address, fee) {
var minFee = constants.tx.minFee;
var dustThreshold = constants.tx.dustThreshold;
var i, size, newkb, change;
var fee;
if (!options || typeof options !== 'object') {
options = {
changeAddress: arguments[1],
fee: arguments[2]
};
}
// Oldest unspents first
unspent = unspent.slice().sort(function(a, b) {
@ -1030,7 +1034,7 @@ TX.prototype.getInputs = function getInputs(unspent, address, fee) {
});
function total() {
if (self.subtractFee)
if (options.subtractFee)
return outputValue;
return outputValue.add(fee);
}
@ -1047,16 +1051,21 @@ TX.prototype.getInputs = function getInputs(unspent, address, fee) {
// funds to cover both minimum post cost
// and fee.
index = tx._addInput(unspent[i]);
inputs.push(tx.inputs[index]);
inputs.push(new bcoin.input(tx.inputs[index]));
lastAdded++;
if (options.wallet)
options.wallet.scriptInputs(tx, index);
// Stop once we're full.
if (isFull())
break;
}
}
if (fee) {
if (options.fee) {
fee = options.fee;
// Transfer `total` funds maximum.
addCoins();
} else {
@ -1068,7 +1077,7 @@ TX.prototype.getInputs = function getInputs(unspent, address, fee) {
// Add dummy output (for `change`) to
// calculate maximum TX size.
tx.addOutput({
address: address,
address: options.changeAddress,
value: new bn(0)
});
@ -1076,7 +1085,7 @@ TX.prototype.getInputs = function getInputs(unspent, address, fee) {
// bytes (10000 satoshi for every 1024 bytes).
do {
// Calculate max possible size after signing.
size = tx.maxSize();
size = tx.maxSize(options.m, options.n);
newkb = Math.ceil(size / 1024) - totalkb;
fee.iaddn(newkb * minFee);
@ -1096,7 +1105,7 @@ TX.prototype.getInputs = function getInputs(unspent, address, fee) {
change = tx.getInputValue().sub(total());
// Attempt to subtract fee.
if (this.subtractFee) {
if (options.subtractFee) {
for (i = 0; i < tx.outputs.length; i++) {
if (tx.outputs[i].value.cmp(fee.addn(dustThreshold)) >= 0) {
tx.outputs[i].value.isub(fee);
@ -1120,27 +1129,29 @@ TX.prototype.getInputs = function getInputs(unspent, address, fee) {
};
};
TX.prototype.fill = function fill(unspent, address, fee) {
TX.prototype.fill = function fill(unspent, options) {
var result;
if (!address)
address = this.changeAddress;
if (!fee)
fee = this.hardFee;
if (!options || typeof options !== 'object') {
options = {
changeAddress: arguments[1],
fee: arguments[2]
};
}
assert(unspent);
assert(address);
assert(options.changeAddress);
result = this.getInputs(unspent, address, fee);
result = this.getInputs(unspent, options);
this.changeAddress = address;
this.hardFee = fee;
this.total = result.total;
if (!result.inputs)
return result;
this._changeAddress = options.changeAddress;
this._fee = options.fee;
result.inputs.forEach(function(input) {
this.addInput(input);
}, this);
@ -1154,7 +1165,7 @@ TX.prototype.fill = function fill(unspent, address, fee) {
this.changeIndex = -1;
} else {
this.addOutput({
address: this.changeAddress,
address: options.changeAddress,
value: result.change
});
@ -1211,12 +1222,15 @@ TX.prototype._recalculateFee = function recalculateFee() {
var output = this.outputs[this.changeIndex];
var size, real, fee;
if (this.hardFee)
if (!this._changeAddress)
return;
if (this._fee)
return;
if (!output) {
this.addOutput({
address: this.changeAddress,
address: this._changeAddress,
value: new bn(0)
});
output = this.outputs[this.outputs.length - 1];
@ -1776,7 +1790,9 @@ TX.prototype.inspect = function inspect() {
copy.block = this.block;
delete copy._raw;
delete copy._chain;
delete copy.unspent;
delete copy._changeAddress;
delete copy._fee;
delete copy.total;
copy.hash = this.hash('hex');
copy.rhash = this.rhash;
copy.rblock = this.rblock;
@ -1785,8 +1801,6 @@ TX.prototype.inspect = function inspect() {
copy.confirmations = this.getConfirmations();
copy.priority = this.getPriority().toString(10);
copy.date = new Date((copy.ts || 0) * 1000).toISOString();
if (copy.hardFee)
copy.hardFee = utils.btc(copy.hardFee);
return copy;
};
@ -1801,9 +1815,7 @@ TX.prototype.toJSON = function toJSON() {
height: this.height,
network: this.network,
relayedBy: this.relayedBy,
changeAddress: this.changeAddress,
changeIndex: this.changeIndex,
hardFee: this.hardFee ? utils.btc(this.hardFee) : null,
coins: this.inputs.map(function(input) {
return input.output ? input.output.toJSON() : null;
}),
@ -1823,12 +1835,8 @@ TX.fromJSON = function fromJSON(json) {
data.network = json.network;
data.relayedBy = json.relayedBy;
data.changeAddress = json.changeAddress;
data.changeIndex = json.changeIndex;
if (json.hardFee)
data.hardFee = utils.satoshi(json.hardFee);
data._raw = raw;
data._size = raw.length;

View File

@ -501,18 +501,20 @@ Wallet.prototype.ownOutput = function ownOutput(tx, index) {
};
Wallet.prototype.fill = function fill(tx, options) {
var address, unspent;
var unspent, result;
if (!options)
options = {};
assert(this._initialized);
address = this.changeAddress.getAddress();
unspent = this.getUnspent();
result = tx.fill(unspent, address, options.fee);
result = tx.fill(unspent, utils.merge(options || {}, {
changeAddress: this.changeAddress.getAddress(),
m: this.m,
n: this.n
}));
if (!result.inputs)
return false;
@ -531,7 +533,7 @@ Wallet.prototype.fillPrevout = function fillPrevout(tx) {
// Legacy
Wallet.prototype.fillTX = Wallet.prototype.fillPrevout;
Wallet.prototype.createTX = function createTX(outputs, fee) {
Wallet.prototype.createTX = function createTX(outputs, options) {
var tx = bcoin.tx();
var target;
@ -544,7 +546,7 @@ Wallet.prototype.createTX = function createTX(outputs, fee) {
});
// Fill the inputs with unspents
if (!this.fill(tx, null, fee))
if (!this.fill(tx, options))
return;
// Sort members a la BIP69