tests. asserts. subtractFee.
This commit is contained in:
parent
8a16cefe30
commit
8b262a7ff9
@ -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()
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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()
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -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]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user