tests. asserts. subtractFee.

This commit is contained in:
Christopher Jeffrey 2016-06-20 16:49:09 -07:00
parent 8a16cefe30
commit 8b262a7ff9
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
7 changed files with 152 additions and 44 deletions

View File

@ -477,7 +477,8 @@ Input.prototype.toJSON = function toJSON() {
coin: this.coin ? this.coin.toJSON() : null, coin: this.coin ? this.coin.toJSON() : null,
script: this.script.toJSON(), script: this.script.toJSON(),
witness: this.witness.toJSON(), witness: this.witness.toJSON(),
sequence: this.sequence sequence: this.sequence,
address: this.getAddress()
}; };
}; };

View File

@ -1091,35 +1091,6 @@ MTX.prototype.selectCoins = function selectCoins(coins, options) {
// How much money is left after filling outputs. // How much money is left after filling outputs.
change = tx.getInputValue() - total(); change = tx.getInputValue() - total();
// Attempt to subtract fee.
if (options.subtractFee != null) {
if (typeof options.subtractFee === 'number') {
i = options.subtractFee;
output = tx.outputs[i];
if (!output)
throw new Error('Subtraction index does not exist.');
min = fee + output.getDustThreshold();
if (output.value < min)
throw new Error('Could not subtract fee.');
output.value -= fee;
} else {
for (i = 0; i < tx.outputs.length; i++) {
output = tx.outputs[i];
min = fee + output.getDustThreshold();
if (output.value >= min) {
output.value -= fee;
break;
}
}
if (i === tx.outputs.length)
throw new Error('Could not subtract fee.');
}
}
// Return necessary inputs and change. // Return necessary inputs and change.
return { return {
coins: chosen, coins: chosen,
@ -1129,6 +1100,44 @@ MTX.prototype.selectCoins = function selectCoins(coins, options) {
}; };
}; };
/**
* Attempt to subtract a fee from outputs.
* @param {Amount} fee
* @param {Number?} index
*/
MTX.prototype.subtractFee = function subtractFee(fee, index) {
var i, min, output;
if (typeof index === 'number') {
output = this.outputs[index];
if (!output)
throw new Error('Subtraction index does not exist.');
min = fee + output.getDustThreshold();
if (output.value < min)
throw new Error('Could not subtract fee.');
output.value -= fee;
return;
}
for (i = 0; i < this.outputs.length; i++) {
output = this.outputs[i];
min = fee + output.getDustThreshold();
if (output.value >= min) {
output.value -= fee;
break;
}
}
if (i === this.outputs.length)
throw new Error('Could not subtract fee.');
};
/** /**
* Select coins and fill the inputs. * Select coins and fill the inputs.
* @param {Coin[]} coins * @param {Coin[]} coins
@ -1164,6 +1173,10 @@ MTX.prototype.fill = function fill(coins, options) {
for (i = 0; i < result.coins.length; i++) for (i = 0; i < result.coins.length; i++)
this.addInput(result.coins[i]); this.addInput(result.coins[i]);
// Attempt to subtract fee.
if (options.subtractFee || options.subtractFee === 0)
this.subtractFee(result.fee, options.subtractFee);
// Add a change output. // Add a change output.
this.addOutput({ this.addOutput({
address: changeAddress, address: changeAddress,

View File

@ -166,7 +166,8 @@ Output.prototype.inspect = function inspect() {
Output.prototype.toJSON = function toJSON() { Output.prototype.toJSON = function toJSON() {
return { return {
value: utils.btc(this.value), value: utils.btc(this.value),
script: this.script.toJSON() script: this.script.toJSON(),
address: this.getAddress()
}; };
}; };

View File

@ -628,7 +628,7 @@ Framer.tx = function _tx(tx, writer) {
*/ */
Framer.witnessTX = function _witnessTX(tx, writer) { Framer.witnessTX = function _witnessTX(tx, writer) {
return tx.toRaw(writer); return tx.toWitness(writer);
}; };
/** /**
@ -651,7 +651,7 @@ Framer.block = function _block(block, writer) {
*/ */
Framer.witnessBlock = function _witnessBlock(block, writer) { Framer.witnessBlock = function _witnessBlock(block, writer) {
return block.toRaw(writer); return block.toWitness(writer);
}; };
/** /**

View File

@ -2422,7 +2422,7 @@ Script.isCode = function isCode(raw) {
*/ */
Script.fromPubkey = function fromPubkey(key) { Script.fromPubkey = function fromPubkey(key) {
assert(key.length >= 33); assert(Buffer.isBuffer(key) && key.length >= 33);
return Script.fromArray([key, opcodes.OP_CHECKSIG]); return Script.fromArray([key, opcodes.OP_CHECKSIG]);
}; };
@ -2433,7 +2433,7 @@ Script.fromPubkey = function fromPubkey(key) {
*/ */
Script.fromPubkeyhash = function fromPubkeyhash(hash) { Script.fromPubkeyhash = function fromPubkeyhash(hash) {
assert(hash.length === 20); assert(Buffer.isBuffer(hash) && hash.length === 20);
return Script.fromArray([ return Script.fromArray([
opcodes.OP_DUP, opcodes.OP_DUP,
opcodes.OP_HASH160, opcodes.OP_HASH160,
@ -2455,6 +2455,8 @@ Script.fromMultisig = function fromMultisig(m, n, keys) {
var code = []; var code = [];
var i; var i;
assert(utils.isNumber(m) && utils.isNumber(n));
assert(Array.isArray(keys));
assert(keys.length === n, '`n` keys are required for multisig.'); assert(keys.length === n, '`n` keys are required for multisig.');
assert(m >= 1 && m <= n); assert(m >= 1 && m <= n);
assert(n >= 1 && n <= 15); assert(n >= 1 && n <= 15);
@ -2479,7 +2481,7 @@ Script.fromMultisig = function fromMultisig(m, n, keys) {
*/ */
Script.fromScripthash = function fromScripthash(hash) { Script.fromScripthash = function fromScripthash(hash) {
assert(hash.length === 20); assert(Buffer.isBuffer(hash) && hash.length === 20);
return Script.fromArray([ return Script.fromArray([
opcodes.OP_HASH160, opcodes.OP_HASH160,
hash, hash,
@ -2510,8 +2512,8 @@ Script.fromNulldata = function fromNulldata(flags) {
*/ */
Script.fromProgram = function fromProgram(version, data) { Script.fromProgram = function fromProgram(version, data) {
assert(typeof version === 'number' && version >= 0 && version <= 16); assert(utils.isNumber(version) && version >= 0 && version <= 16);
assert(data.length >= 2 && data.length <= 32); assert(Buffer.isBuffer(data) && data.length >= 2 && data.length <= 40);
return Script.fromArray([version === 0 ? 0 : version + 0x50, data]); return Script.fromArray([version === 0 ? 0 : version + 0x50, data]);
}; };

View File

@ -57,6 +57,7 @@ function TX(options) {
this.inputs = []; this.inputs = [];
this.outputs = []; this.outputs = [];
this.locktime = 0; this.locktime = 0;
this.ts = 0; this.ts = 0;
this.block = null; this.block = null;
this.index = -1; this.index = -1;
@ -100,7 +101,15 @@ TX.prototype.fromOptions = function fromOptions(options) {
this.version = options.version; this.version = options.version;
this.flag = options.flag; this.flag = options.flag;
for (i = 0; i < options.inputs.length; i++)
this.inputs.push(new bcoin.input(options.inputs[i]));
for (i = 0; i < options.outputs.length; i++)
this.outputs.push(new bcoin.output(options.outputs[i]));
this.locktime = options.locktime; this.locktime = options.locktime;
this.ts = options.ts || 0; this.ts = options.ts || 0;
this.block = options.block || null; this.block = options.block || null;
this.index = options.index != null this.index = options.index != null
@ -121,12 +130,6 @@ TX.prototype.fromOptions = function fromOptions(options) {
? options._witnessSize ? options._witnessSize
: null; : null;
for (i = 0; i < options.inputs.length; i++)
this.inputs.push(new bcoin.input(options.inputs[i]));
for (i = 0; i < options.outputs.length; i++)
this.outputs.push(new bcoin.output(options.outputs[i]));
return this; return this;
}; };
@ -1890,6 +1893,8 @@ TX.prototype.toJSON = function toJSON() {
ps: this.ps, ps: this.ps,
index: this.index, index: this.index,
changeIndex: this.changeIndex || -1, changeIndex: this.changeIndex || -1,
fee: utils.btc(this.getFee()),
confirmations: this.getConfirmations(),
version: this.version, version: this.version,
flag: this.flag, flag: this.flag,
inputs: this.inputs.map(function(input) { inputs: this.inputs.map(function(input) {
@ -2286,8 +2291,10 @@ TX.fromExtended = function fromExtended(data, saveCoins, enc) {
enc = saveCoins; enc = saveCoins;
saveCoins = false; saveCoins = false;
} }
if (typeof data === 'string') if (typeof data === 'string')
data = new Buffer(data, enc); data = new Buffer(data, enc);
return new TX().fromExtended(data, saveCoins); return new TX().fromExtended(data, saveCoins);
}; };

View File

@ -903,6 +903,90 @@ describe('Wallet', function() {
}); });
}); });
it('should fill tx with inputs with subtract fee', function(cb) {
walletdb.create(function(err, w1) {
assert.ifError(err);
walletdb.create(function(err, w2) {
assert.ifError(err);
// Coinbase
var t1 = bcoin.mtx()
.addOutput(w1, 5460)
.addOutput(w1, 5460)
.addOutput(w1, 5460)
.addOutput(w1, 5460);
t1.addInput(dummyInput);
walletdb.addTX(t1, function(err) {
assert.ifError(err);
// Create new transaction
var t2 = bcoin.mtx().addOutput(w2, 21840);
w1.fill(t2, { rate: 10000, round: true, subtractFee: true }, function(err) {
assert.ifError(err);
w1.sign(t2, function(err) {
assert.ifError(err);
assert(t2.verify());
assert.equal(t2.getInputValue(), 5460 * 4);
assert.equal(t2.getOutputValue(), 21840 - 10000);
assert.equal(t2.getFee(), 10000);
cb();
});
});
});
});
});
});
it('should fill tx with inputs with subtract fee with create tx', function(cb) {
walletdb.create(function(err, w1) {
assert.ifError(err);
walletdb.create(function(err, w2) {
assert.ifError(err);
// Coinbase
var t1 = bcoin.mtx()
.addOutput(w1, 5460)
.addOutput(w1, 5460)
.addOutput(w1, 5460)
.addOutput(w1, 5460);
t1.addInput(dummyInput);
walletdb.addTX(t1, function(err) {
assert.ifError(err);
var options = {
subtractFee: true,
rate: 10000,
round: true,
outputs: [{ address: w2.getAddress(), value: 21840 }]
};
// Create new transaction
w1.createTX(options, function(err, t2) {
assert.ifError(err);
w1.sign(t2, function(err) {
assert.ifError(err);
assert(t2.verify());
assert.equal(t2.getInputValue(), 5460 * 4);
assert.equal(t2.getOutputValue(), 21840 - 10000);
assert.equal(t2.getFee(), 10000);
cb();
});
});
});
});
});
});
it('should cleanup', function(cb) { it('should cleanup', function(cb) {
constants.tx.COINBASE_MATURITY = 100; constants.tx.COINBASE_MATURITY = 100;
cb(); cb();