fill refactor. accurate fee calculation.
This commit is contained in:
parent
c4818c4bc8
commit
016c85f8b5
@ -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;
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user