more tx refactoring.

This commit is contained in:
Christopher Jeffrey 2016-01-12 05:15:22 -08:00
parent b147951a83
commit c94457cf1f
2 changed files with 63 additions and 57 deletions

View File

@ -41,10 +41,6 @@ function TX(data, block) {
this._lock = this.lock; this._lock = this.lock;
this._fee = !(data instanceof bcoin.tx)
? data.fee
: data._fee;
if (data.inputs) { if (data.inputs) {
data.inputs.forEach(function(input) { data.inputs.forEach(function(input) {
this.input(input, null); this.input(input, null);
@ -64,8 +60,9 @@ function TX(data, block) {
} }
} }
this.hardFee = data.hardFee || null;
this.changeAddress = data.changeAddress || null; this.changeAddress = data.changeAddress || null;
this.changeOutput = data.changeOutput || null; this.changeIndex = data.changeIndex || -1;
// ps = Pending Since // ps = Pending Since
this.ps = this.ts === 0 ? utils.now() : 0; this.ps = this.ts === 0 ? utils.now() : 0;
@ -785,7 +782,7 @@ TX.prototype.maxSize = function maxSize() {
return total; return total;
}; };
TX.prototype.getUnspent = function getUnspent(unspent, fee) { TX.prototype.getUnspent = function getUnspent(unspent, address, fee) {
var tx = this.clone(); var tx = this.clone();
var cost = tx.funds('out'); var cost = tx.funds('out');
var totalkb = 1; var totalkb = 1;
@ -794,12 +791,9 @@ TX.prototype.getUnspent = function getUnspent(unspent, fee) {
var lastAdded = 0; var lastAdded = 0;
var size, newkb, change; var size, newkb, change;
if (!fee)
fee = this._fee;
if (fee) { if (fee) {
total = cost.add(fee); total = cost.add(fee);
this._fee = fee; this.hardFee = fee;
} }
function addInput(unspent) { function addInput(unspent) {
@ -819,7 +813,7 @@ TX.prototype.getUnspent = function getUnspent(unspent, fee) {
// Add dummy output (for `change`) to // Add dummy output (for `change`) to
// calculate maximum TX size. // calculate maximum TX size.
tx.output({ tx.output({
script: [], address: address,
value: new bn(0) value: new bn(0)
}); });
@ -839,15 +833,13 @@ TX.prototype.getUnspent = function getUnspent(unspent, fee) {
} while (tx.funds('in').cmp(total) < 0 && lastAdded < unspent.length); } while (tx.funds('in').cmp(total) < 0 && lastAdded < unspent.length);
} }
// Expose `total`. Useful for error messages. if (tx.funds('in').cmp(total) < 0) {
this.total = total; // Still failing to get enough funds.
inputs = null;
// Still failing to get enough funds. } else {
if (tx.funds('in').cmp(total) < 0) // How much money is left after filling outputs.
return; change = tx.funds('in').sub(total);
}
// How much money is left after filling outputs.
change = tx.funds('in').sub(total);
// Return necessary inputs and change. // Return necessary inputs and change.
return { return {
@ -855,22 +847,26 @@ TX.prototype.getUnspent = function getUnspent(unspent, fee) {
change: change, change: change,
cost: cost, cost: cost,
fee: total.sub(cost), fee: total.sub(cost),
total: total total: total,
kb: totalkb
}; };
}; };
TX.prototype.fillUnspent = function fillUnspent(unspent, changeAddress, fee) { TX.prototype.fillUnspent = function fillUnspent(unspent, address, fee) {
var result = this.getUnspent(unspent, fee); var result;
if (address)
this.changeAddress = address;
if (fee) if (fee)
this._fee = fee; this.hardFee = fee;
if (!result) assert(this.changeAddress);
return;
this.changeAddress = changeAddress result = this.getUnspent(unspent, this.changeAddress, this.hardFee);
|| this.changeAddress
|| result.inputs[0].output.address; if (!result.inputs)
return result;
result.inputs.forEach(function(input) { result.inputs.forEach(function(input) {
this.input(input); this.input(input);
@ -882,27 +878,24 @@ TX.prototype.fillUnspent = function fillUnspent(unspent, changeAddress, fee) {
this.getFee().toNumber(), this.getFee().toNumber(),
result.fee.add(result.change).toNumber() result.fee.add(result.change).toNumber()
); );
this.changeOutput = null; this.changeIndex = -1;
} else { } else {
if (!this.changeAddress)
throw new Error('No change address');
this.output({ this.output({
address: this.changeAddress, address: this.changeAddress,
value: result.change value: result.change
}); });
this.changeOutput = this.outputs[this.outputs.length - 1]; this.changeIndex = this.outputs.length - 1;
} }
return result; return result;
}; };
TX.prototype._recalculateFee = function recalculateFee() { TX.prototype._recalculateFee = function recalculateFee() {
var output = this.changeOutput; var output = this.outputs[this.changeIndex];
var size, real, fee; var size, real, fee;
if (this._fee) if (this.hardFee)
return; return;
if (!output) { if (!output) {
@ -918,7 +911,7 @@ TX.prototype._recalculateFee = function recalculateFee() {
fee = this.getFee().toNumber(); fee = this.getFee().toNumber();
if (real === fee) { if (real === fee) {
if (!this.changeOutput) if (this.changeIndex === -1)
this.outputs.pop(); this.outputs.pop();
return; return;
} }
@ -926,7 +919,7 @@ TX.prototype._recalculateFee = function recalculateFee() {
if (real > fee) { if (real > fee) {
if (output.value.cmpn(real - fee) < 0) { if (output.value.cmpn(real - fee) < 0) {
this.outputs.pop(); this.outputs.pop();
this.changeOutput = null; this.changeIndex = -1;
return; return;
} }
output.value.isubn(real - fee); output.value.isubn(real - fee);
@ -936,11 +929,11 @@ TX.prototype._recalculateFee = function recalculateFee() {
if (output.value.cmpn(constants.tx.dust) < 0) { if (output.value.cmpn(constants.tx.dust) < 0) {
this.outputs.pop(); this.outputs.pop();
this.changeOutput = null; this.changeIndex = -1;
return; return;
} }
this.changeOutput = output; this.changeIndex = this.outputs.indexOf(output);
}; };
TX.prototype.getFee = function getFee() { TX.prototype.getFee = function getFee() {
@ -1247,7 +1240,9 @@ TX.prototype.toJSON = function toJSON() {
block: this.block, block: this.block,
network: this.network, network: this.network,
relayedBy: this.relayedBy, relayedBy: this.relayedBy,
changeIndex: this.outputs.indexOf(this.changeOutput), changeAddress: this.changeAddress,
changeIndex: this.changeIndex,
hardFee: this.hardFee ? utils.btc(this.hardFee) : null,
tx: utils.toHex(this.render()) tx: utils.toHex(this.render())
}; };
}; };
@ -1264,6 +1259,12 @@ 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;
if (json.hardFee)
data.hardFee = utils.satoshi(json.hardFee);
data._raw = raw; data._raw = raw;
data._size = raw.length; data._size = raw.length;
@ -1272,14 +1273,24 @@ TX.fromJSON = function fromJSON(json) {
tx.block = json.block || null; tx.block = json.block || null;
tx.ps = json.ps; tx.ps = json.ps;
if (data.changeIndex >= 0) {
tx.changeOutput = tx.outputs[data.changeIndex];
assert(tx.changeOutput);
}
return tx; return tx;
}; };
TX.prototype.toRaw = function toRaw(enc) {
var raw = this.render();
if (enc === 'hex')
return utils.toHex(raw);
return raw;
};
TX.fromRaw = function fromRaw(raw, enc) {
if (enc === 'hex')
raw = utils.toArray(raw, 'hex');
return new bcoin.protocol.parser().parseTX(raw);
};
/** /**
* Expose * Expose
*/ */

View File

@ -436,16 +436,11 @@ Wallet.prototype.ownInput = function ownInput(tx, index) {
return inputs; return inputs;
}; };
Wallet.prototype.scriptOutputs = function scriptOutputs(tx, options) { Wallet.prototype.fillUnspent = function fillUnspent(tx, address, fee) {
outputs.forEach(function(output) { if (!address)
tx.scriptOutput(output, output); address = this.changeAddress || this.getAddress();
});
return outputs.length;
};
Wallet.prototype.fillUnspent = function fillUnspent(tx, changeAddress) { return tx.fillUnspent(this.unspent(), address, fee);
changeAddress = changeAddress || this.changeAddress || this.getAddress();
return tx.fillUnspent(this.unspent(), changeAddress);
}; };
Wallet.prototype.fillTX = function fillTX(tx) { Wallet.prototype.fillTX = function fillTX(tx) {
@ -546,9 +541,9 @@ Wallet.prototype.fill = function fill(tx, changeAddress, cb) {
result = this.fillUnspent(tx, changeAddress); result = this.fillUnspent(tx, changeAddress);
if (!result) { if (!result.inputs) {
err = new Error('Not enough funds'); err = new Error('Not enough funds');
err.minBalance = tx.total; err.minBalance = result.total;
cb(err); cb(err);
return null; return null;
} }