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

View File

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