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.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;
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user