coinselect
Advanced tools
Comparing version 0.2.0 to 0.3.0
10
index.js
@@ -38,3 +38,4 @@ // TODO: integrate privacy calculations, group by address, avoid linking multiple addresses together | ||
var accum = 0 | ||
var total = target | ||
var baseFee = estimateRelayFee(byteLength, feePerKb) | ||
var total = target + baseFee | ||
@@ -51,3 +52,3 @@ for (var i = 0; i < sorted.length; ++i) { | ||
var baseFee = estimateRelayFee(byteLength, feePerKb) | ||
baseFee = estimateRelayFee(byteLength, feePerKb) | ||
total = target + baseFee | ||
@@ -82,3 +83,6 @@ | ||
return undefined | ||
return { | ||
fee: baseFee, | ||
inputs: null | ||
} | ||
} |
{ | ||
"name": "coinselect", | ||
"version": "0.2.0", | ||
"version": "0.3.0", | ||
"description": "A fee optimizing bitcoin input selection module", | ||
@@ -5,0 +5,0 @@ "main": "./index.js", |
@@ -9,1 +9,51 @@ # coinselect | ||
A fee-saving bitcoin input selection module. | ||
The code is stable. | ||
The module's interface/existence is not. | ||
Please let me know if you are using this package. | ||
## Example | ||
``` javascript | ||
var coinSelect = require('coinselect') | ||
var feePerKb = 1000 | ||
var unspents = [ | ||
..., | ||
{ | ||
..., | ||
value: 10000 | ||
} | ||
] | ||
var outputs = [ | ||
..., | ||
{ | ||
address: '1EHNa6Q4Jz2uvNExL497mE43ikXhwF6kZm', | ||
value: 5000 | ||
} | ||
] | ||
var result = coinselect(unspents, outputs, feePerKb) | ||
// the accumulated fee is always returned | ||
console.log(result.fee) | ||
// .inputs may be null if not enough funds exist | ||
if (!result.inputs) return | ||
// success! | ||
var txb = new bitcoin.TransactionBuilder() | ||
// is a change output non-dust? | ||
if (result.remainder > 5460) { | ||
txb.addOutput(changeAddress, result.remainder) | ||
} | ||
``` | ||
Feedback welcome on the API, I'm not sure if I like it. | ||
## License [MIT](LICENSE) |
[ | ||
{ | ||
"description": "singular output | no remainder", | ||
"description": "singular output, no remainder", | ||
"feePerKb": 10000, | ||
@@ -14,3 +14,3 @@ "outputs": [100000], | ||
{ | ||
"description": "singular output | remainder expected", | ||
"description": "singular output, remainder expected", | ||
"feePerKb": 10000, | ||
@@ -26,3 +26,3 @@ "outputs": [90000], | ||
{ | ||
"description": "singular output | fee > 10000 | no remainder", | ||
"description": "singular output, fee > 10000, no remainder", | ||
"feePerKb": 10000, | ||
@@ -38,3 +38,3 @@ "outputs": [60000], | ||
{ | ||
"description": "singular output | fee > 10000 | remainder expected", | ||
"description": "singular output, fee > 10000, remainder expected", | ||
"feePerKb": 10000, | ||
@@ -50,3 +50,3 @@ "outputs": [55000], | ||
{ | ||
"description": "multiple outputs | no remainder", | ||
"description": "multiple outputs, no remainder", | ||
"feePerKb": 10000, | ||
@@ -62,3 +62,3 @@ "outputs": [35000, 5000, 5000, 1000], | ||
{ | ||
"description": "multiple outputs | remainder expected", | ||
"description": "multiple outputs, remainder expected", | ||
"feePerKb": 10000, | ||
@@ -74,3 +74,3 @@ "outputs": [35000, 5000, 2000, 1000], | ||
{ | ||
"description": "multiple outputs | base fee | remainder expected", | ||
"description": "multiple outputs, base fee, remainder expected", | ||
"feePerKb": 1000, | ||
@@ -86,3 +86,3 @@ "unspents": [5000, 5000, 5000, 5000, 5000, 5000], | ||
{ | ||
"description": "multiple outputs | change fee | remainder exists", | ||
"description": "multiple outputs, change fee, remainder exists", | ||
"feePerKb": 1000, | ||
@@ -98,3 +98,3 @@ "unspents": [7000, 5000, 5000, 5000, 5000, 5000], | ||
{ | ||
"description": "no outputs | remainder expected", | ||
"description": "no outputs, remainder expected", | ||
"feePerKb": 10000, | ||
@@ -110,13 +110,41 @@ "outputs": [], | ||
{ | ||
"description": "no solution | target too high (40000 > 20000)", | ||
"description": "not enough funds, empty result", | ||
"feePerKb": 10000, | ||
"outputs": [40000], | ||
"unspents": [20000] | ||
"unspents": [20000], | ||
"expected": { | ||
"fee": 10000, | ||
"inputs": null | ||
} | ||
}, | ||
{ | ||
"description": "no solution | target too high (w/ fee) (40000 + fee > 40000)", | ||
"description": "not enough funds (w/ fee), empty result", | ||
"feePerKb": 10000, | ||
"outputs": [40000], | ||
"unspents": [40000] | ||
"unspents": [40000], | ||
"expected": { | ||
"fee": 10000, | ||
"inputs": null | ||
} | ||
}, | ||
{ | ||
"description": "no unspents, empty result", | ||
"feePerKb": 10000, | ||
"outputs": [], | ||
"unspents": [], | ||
"expected": { | ||
"fee": 10000, | ||
"inputs": null | ||
} | ||
}, | ||
{ | ||
"description": "no unspents, empty result (>1KiB)", | ||
"feePerKb": 10000, | ||
"outputs": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], | ||
"unspents": [], | ||
"expected": { | ||
"fee": 20000, | ||
"inputs": null | ||
} | ||
} | ||
] |
@@ -14,3 +14,3 @@ /* global beforeEach, describe, it */ | ||
outputs = f.outputs.map(function (value) { return { value: value } }) | ||
unspents = f.unspents.map(function (value) { return { value: value } }) | ||
unspents = f.unspents.map(function (value, i) { return { i: i, value: value } }) | ||
}) | ||
@@ -21,20 +21,10 @@ | ||
// ensure a solution was found | ||
if (!f.expected) return assert.equal(result, undefined) | ||
// map to indexes for fixture comparison | ||
if (result.inputs) { | ||
result.inputs = result.inputs.map(function (input) { return input.i }) | ||
} | ||
// ensure remainder is correctly calculated | ||
assert.equal(result.remainder, f.expected.remainder, 'Invalid remainder: ' + result.remainder + ' !== ' + f.expected.remainder) | ||
// ensure fee is correctly calculated | ||
assert.equal(result.fee, f.expected.fee, 'Invalid fee: ' + result.fee + ' !== ' + f.expected.fee) | ||
// ensure all expected inputs are found | ||
f.expected.inputs.forEach(function (i, j) { | ||
assert.equal(result.inputs[j], unspents[i]) | ||
}) | ||
// ensure no other inputs exist | ||
assert.equal(result.inputs.length, f.expected.inputs.length) | ||
assert.deepEqual(result, f.expected) | ||
}) | ||
}) | ||
}) |
10233
230
59