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