add less-than-dust to fee. recalculate fee after input scripts are templated.
This commit is contained in:
parent
3f44538b46
commit
d7eea0941e
@ -42,12 +42,13 @@ function TX(data, block) {
|
||||
// ps = Pending Since
|
||||
this.ps = this.ts === 0 ? +new Date() / 1000 : 0;
|
||||
|
||||
this.change = data.change || null;
|
||||
this.fee = data.fee || 10000;
|
||||
this.dust = 5460;
|
||||
this.changeAddress = data.changeAddress || null;
|
||||
}
|
||||
module.exports = TX;
|
||||
|
||||
TX.fee = 10000;
|
||||
TX.dust = 5460;
|
||||
|
||||
TX.prototype.clone = function clone() {
|
||||
return new TX(this);
|
||||
};
|
||||
@ -155,6 +156,7 @@ TX.prototype.scriptInput = function(input, pub) {
|
||||
// P2PKH and simple tx
|
||||
if (bcoin.script.isPubkeyhash(s) || bcoin.script.isSimplePubkeyhash(s)) {
|
||||
input.script = [ [], pub ];
|
||||
this._recalculateFee();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -171,6 +173,7 @@ TX.prototype.scriptInput = function(input, pub) {
|
||||
n = n[0] || 0;
|
||||
for (var i = 0; i < n; i++)
|
||||
input.script[i + 1] = [];
|
||||
this._recalculateFee();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -187,6 +190,7 @@ TX.prototype.scriptInput = function(input, pub) {
|
||||
input.script[i + 1] = [];
|
||||
// P2SH requires the redeem script after signatures
|
||||
input.script.push(pub);
|
||||
this._recalculateFee();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -568,7 +572,7 @@ TX.prototype.utxos = function utxos(unspent) {
|
||||
var fee = 1;
|
||||
|
||||
// total = cost + fee
|
||||
var total = cost.add(new bn(this.fee));
|
||||
var total = cost.add(new bn(TX.fee));
|
||||
|
||||
var inputs = this.inputs.slice();
|
||||
var utxos = [];
|
||||
@ -587,7 +591,7 @@ TX.prototype.utxos = function utxos(unspent) {
|
||||
// var unspent = wallet.unspent();
|
||||
unspent.every(addInput, this);
|
||||
|
||||
// Add dummy output (for `left`) to calculate maximum TX size
|
||||
// Add dummy output (for `change`) to calculate maximum TX size
|
||||
this.output({ address: null, value: new bn(0) });
|
||||
|
||||
// Change fee value if it is more than 1024 bytes
|
||||
@ -597,7 +601,7 @@ TX.prototype.utxos = function utxos(unspent) {
|
||||
var byteSize = this.maxSize();
|
||||
|
||||
var addFee = Math.ceil(byteSize / 1024) - fee;
|
||||
total.iadd(new bn(addFee * this.fee));
|
||||
total.iadd(new bn(addFee * TX.fee));
|
||||
fee += addFee;
|
||||
|
||||
// Failed to get enough funds, add more inputs
|
||||
@ -609,28 +613,31 @@ TX.prototype.utxos = function utxos(unspent) {
|
||||
if (this.funds('in').cmp(total) < 0) {
|
||||
this.inputs = inputs;
|
||||
this.outputs.pop();
|
||||
this.cost = total;
|
||||
this.total = total;
|
||||
return null;
|
||||
}
|
||||
|
||||
// How much money is left after sending outputs
|
||||
var left = this.funds('in').sub(total);
|
||||
var change = this.funds('in').sub(total);
|
||||
|
||||
// Clear the tx of everything we added.
|
||||
this.inputs = inputs;
|
||||
this.outputs.pop();
|
||||
this.cost = total;
|
||||
|
||||
// Return necessary utxos and change.
|
||||
return {
|
||||
utxos: utxos,
|
||||
change: left,
|
||||
cost: total
|
||||
change: change,
|
||||
cost: cost,
|
||||
fee: total.sub(cost),
|
||||
total: total
|
||||
};
|
||||
};
|
||||
|
||||
TX.prototype.fillUnspent = function fillUnspent(unspent, change) {
|
||||
var result = this.utxos(unspent);
|
||||
TX.prototype.fillUnspent = function fillUnspent(unspent, changeAddress) {
|
||||
var result = unspent.utxos ? unspent : this.utxos(unspent);
|
||||
|
||||
this.changeAddress = changeAddress || this.changeAddress;
|
||||
|
||||
if (!result)
|
||||
return result;
|
||||
@ -639,20 +646,63 @@ TX.prototype.fillUnspent = function fillUnspent(unspent, change) {
|
||||
this.input(utxo, null);
|
||||
}, this);
|
||||
|
||||
// Not enough money, transfer everything to owner
|
||||
if (result.change.cmpn(this.dust) < 0) {
|
||||
// NOTE: that this output is either `postCost` or one of the `dust` values
|
||||
this.outputs[this.outputs.length - 1].value.iadd(result.change);
|
||||
if (result.change.cmpn(TX.dust) < 0) {
|
||||
// Do nothing. Change is added to fee.
|
||||
assert(this.getFee().cmp(result.fee.add(result.change)) === 0);
|
||||
// Adding change to outputs.
|
||||
// this.outputs[this.outputs.length - 1].value.iadd(result.change);
|
||||
this.changeOutput = null;
|
||||
} else {
|
||||
this.output({
|
||||
address: change || this.change,
|
||||
address: this.changeAddress,
|
||||
value: result.change
|
||||
});
|
||||
this.changeOutput = this.outputs[this.outputs.length - 1];
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
TX.prototype._recalculateFee = function recalculateFee() {
|
||||
var output = this.changeOutput;
|
||||
if (!output) {
|
||||
this.output({
|
||||
address: this.changeAddress,
|
||||
value: new bn(0)
|
||||
});
|
||||
output = this.outputs[this.outputs.length - 1];
|
||||
}
|
||||
|
||||
var byteSize = this.maxSize();
|
||||
var newFee = Math.ceil(byteSize / 1024) * TX.fee;
|
||||
var currentFee = this.getFee().toNumber();
|
||||
|
||||
if (newFee === currentFee) {
|
||||
if (!this.changeOutput)
|
||||
this.outputs.pop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (newFee > currentFee) {
|
||||
if (output.value.cmpn(newFee - currentFee) < 0) {
|
||||
this.outputs.pop();
|
||||
this.changeOutput = null;
|
||||
return;
|
||||
}
|
||||
output.value.isubn(newFee - currentFee);
|
||||
} else {
|
||||
output.value.iaddn(currentFee - newFee);
|
||||
}
|
||||
|
||||
if (output.value.cmpn(TX.dust) < 0) {
|
||||
this.outputs.pop();
|
||||
this.changeOutput = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this.changeOutput = output;
|
||||
};
|
||||
|
||||
TX.prototype.inputAddrs = function inputAddrs() {
|
||||
return this.inputs.filter(function(input) {
|
||||
return bcoin.script.isPubkeyhashInput(input.script);
|
||||
@ -663,6 +713,10 @@ TX.prototype.inputAddrs = function inputAddrs() {
|
||||
});
|
||||
};
|
||||
|
||||
TX.prototype.getFee = function getFee() {
|
||||
return this.funds('in').sub(this.funds('out'));
|
||||
};
|
||||
|
||||
TX.prototype.funds = function funds(side) {
|
||||
if (side === 'in') {
|
||||
var inputs = this.inputs.filter(function(input) {
|
||||
|
||||
@ -406,7 +406,7 @@ Wallet.prototype.fill = function fill(tx, cb) {
|
||||
var result = tx.fillUnspent(this.unspent(), this.getAddress());
|
||||
if (!result) {
|
||||
var err = new Error('Not enough funds');
|
||||
err.minBalance = tx.cost;
|
||||
err.minBalance = tx.total;
|
||||
cb(err);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -177,7 +177,10 @@ describe('Wallet', function() {
|
||||
assert(t2.verify());
|
||||
|
||||
assert.equal(t2.funds('in').toString(10), 16380);
|
||||
assert.equal(t2.funds('out').toString(10), 6380);
|
||||
// If change < dust and is added to outputs:
|
||||
// assert.equal(t2.funds('out').toString(10), 6380);
|
||||
// If change < dust and is added to fee:
|
||||
assert.equal(t2.funds('out').toString(10), 5460);
|
||||
|
||||
// Create new transaction
|
||||
var t3 = bcoin.tx().out(w2, 15000);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user