coinselect
Advanced tools
Comparing version 0.1.1 to 0.2.0
21
index.js
@@ -33,3 +33,3 @@ // TODO: integrate privacy calculations, group by address, avoid linking multiple addresses together | ||
outputs.forEach(function (output) { | ||
byteLength += TX_PUBKEYHASH_OUTPUT | ||
byteLength += output.script ? output.script.length : TX_PUBKEYHASH_OUTPUT | ||
target += output.value | ||
@@ -44,2 +44,3 @@ }) | ||
// TODO: an estimate is used because of missing signature data | ||
byteLength += TX_PUBKEYHASH_INPUT | ||
@@ -58,16 +59,20 @@ accum += unspent.value | ||
var feeAfterChange = estimateRelayFee(byteLength + TX_PUBKEYHASH_OUTPUT, feePerKb) | ||
var totalAfterChange = target + feeAfterChange | ||
var feeWithChange = estimateRelayFee(byteLength + TX_PUBKEYHASH_OUTPUT, feePerKb) | ||
var totalWithChange = target + feeWithChange | ||
// can we afford a change output? | ||
if (accum >= totalAfterChange) { | ||
if (accum >= totalWithChange) { | ||
var remainderWithChange = accum - totalWithChange | ||
return { | ||
fee: feeAfterChange, | ||
fee: feeWithChange, | ||
inputs: inputs, | ||
remainder: accum - totalAfterChange | ||
remainder: remainderWithChange | ||
} | ||
} | ||
var remainder = accum - total | ||
return { | ||
fee: baseFee + (accum - total), | ||
fee: baseFee + remainder, | ||
inputs: inputs, | ||
@@ -78,3 +83,3 @@ remainder: 0 | ||
throw new Error('Not enough funds: ' + accum + ' < ' + total) | ||
return undefined | ||
} |
{ | ||
"name": "coinselect", | ||
"version": "0.1.1", | ||
"description": "A fee optimizing bitcoin input selection module.", | ||
"version": "0.2.0", | ||
"description": "A fee optimizing bitcoin input selection module", | ||
"main": "./index.js", | ||
@@ -6,0 +6,0 @@ "scripts": { |
@@ -1,117 +0,113 @@ | ||
{ | ||
"valid": [ | ||
{ | ||
"description": "singular output | no remainder", | ||
"feePerKb": 10000, | ||
"outputs": [100000], | ||
"unspents": [30000, 110000], | ||
"expected": { | ||
"fee": 10000, | ||
"inputs": [1], | ||
"remainder": 0 | ||
} | ||
}, | ||
{ | ||
"description": "singular output | remainder expected", | ||
"feePerKb": 10000, | ||
"outputs": [90000], | ||
"unspents": [30000, 110000], | ||
"expected": { | ||
"fee": 10000, | ||
"inputs": [1], | ||
"remainder": 10000 | ||
} | ||
}, | ||
{ | ||
"description": "singular output | fee > 10000 | no remainder", | ||
"feePerKb": 10000, | ||
"outputs": [60000], | ||
"unspents": [10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000], | ||
"expected": { | ||
"fee": 20000, | ||
"inputs": [0, 1, 2, 3, 4, 5, 6, 7], | ||
"remainder": 0 | ||
} | ||
}, | ||
{ | ||
"description": "singular output | fee > 10000 | remainder expected", | ||
"feePerKb": 10000, | ||
"outputs": [55000], | ||
"unspents": [10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000], | ||
"expected": { | ||
"fee": 20000, | ||
"inputs": [0, 1, 2, 3, 4, 5, 6, 7], | ||
"remainder": 5000 | ||
} | ||
}, | ||
{ | ||
"description": "multiple outputs | no remainder", | ||
"feePerKb": 10000, | ||
"outputs": [35000, 5000, 5000, 1000], | ||
"unspents": [30000, 16000, 10000], | ||
"expected": { | ||
"fee": 10000, | ||
"inputs": [0, 1, 2], | ||
"remainder": 0 | ||
} | ||
}, | ||
{ | ||
"description": "multiple outputs | remainder expected", | ||
"feePerKb": 10000, | ||
"outputs": [35000, 5000, 2000, 1000], | ||
"unspents": [30000, 16000, 10000], | ||
"expected": { | ||
"fee": 10000, | ||
"inputs": [0, 1, 2], | ||
"remainder": 3000 | ||
} | ||
}, | ||
{ | ||
"description": "multiple outputs | base fee | remainder expected", | ||
"feePerKb": 1000, | ||
"unspents": [5000, 5000, 5000, 5000, 5000, 5000], | ||
"outputs": [28000, 1000], | ||
"expected": { | ||
"fee": 1000, | ||
"inputs": [0, 1, 2, 3, 4, 5], | ||
"remainder": 0 | ||
} | ||
}, | ||
{ | ||
"description": "multiple outputs | change fee | remainder exists", | ||
"feePerKb": 1000, | ||
"unspents": [7000, 5000, 5000, 5000, 5000, 5000], | ||
"outputs": [28000, 1000], | ||
"expected": { | ||
"fee": 2000, | ||
"inputs": [0, 1, 2, 3, 4, 5], | ||
"remainder": 1000 | ||
} | ||
}, | ||
{ | ||
"description": "no outputs | remainder expected", | ||
"feePerKb": 10000, | ||
"outputs": [], | ||
"unspents": [30000, 16000, 10000], | ||
"expected": { | ||
"fee": 10000, | ||
"inputs": [0], | ||
"remainder": 20000 | ||
} | ||
[ | ||
{ | ||
"description": "singular output | no remainder", | ||
"feePerKb": 10000, | ||
"outputs": [100000], | ||
"unspents": [30000, 110000], | ||
"expected": { | ||
"fee": 10000, | ||
"inputs": [1], | ||
"remainder": 0 | ||
} | ||
], | ||
"invalid": [ | ||
{ | ||
"exception": "Not enough funds: 20000 < 40000", | ||
"feePerKb": 10000, | ||
"outputs": [40000], | ||
"unspents": [10000, 10000] | ||
}, | ||
{ | ||
"exception": "Not enough funds: 40000 < 50000", | ||
"feePerKb": 10000, | ||
"outputs": [40000], | ||
"unspents": [40000] | ||
}, | ||
{ | ||
"description": "singular output | remainder expected", | ||
"feePerKb": 10000, | ||
"outputs": [90000], | ||
"unspents": [30000, 110000], | ||
"expected": { | ||
"fee": 10000, | ||
"inputs": [1], | ||
"remainder": 10000 | ||
} | ||
] | ||
} | ||
}, | ||
{ | ||
"description": "singular output | fee > 10000 | no remainder", | ||
"feePerKb": 10000, | ||
"outputs": [60000], | ||
"unspents": [10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000], | ||
"expected": { | ||
"fee": 20000, | ||
"inputs": [0, 1, 2, 3, 4, 5, 6, 7], | ||
"remainder": 0 | ||
} | ||
}, | ||
{ | ||
"description": "singular output | fee > 10000 | remainder expected", | ||
"feePerKb": 10000, | ||
"outputs": [55000], | ||
"unspents": [10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000], | ||
"expected": { | ||
"fee": 20000, | ||
"inputs": [0, 1, 2, 3, 4, 5, 6, 7], | ||
"remainder": 5000 | ||
} | ||
}, | ||
{ | ||
"description": "multiple outputs | no remainder", | ||
"feePerKb": 10000, | ||
"outputs": [35000, 5000, 5000, 1000], | ||
"unspents": [30000, 16000, 10000], | ||
"expected": { | ||
"fee": 10000, | ||
"inputs": [0, 1, 2], | ||
"remainder": 0 | ||
} | ||
}, | ||
{ | ||
"description": "multiple outputs | remainder expected", | ||
"feePerKb": 10000, | ||
"outputs": [35000, 5000, 2000, 1000], | ||
"unspents": [30000, 16000, 10000], | ||
"expected": { | ||
"fee": 10000, | ||
"inputs": [0, 1, 2], | ||
"remainder": 3000 | ||
} | ||
}, | ||
{ | ||
"description": "multiple outputs | base fee | remainder expected", | ||
"feePerKb": 1000, | ||
"unspents": [5000, 5000, 5000, 5000, 5000, 5000], | ||
"outputs": [28000, 1000], | ||
"expected": { | ||
"fee": 1000, | ||
"inputs": [0, 1, 2, 3, 4, 5], | ||
"remainder": 0 | ||
} | ||
}, | ||
{ | ||
"description": "multiple outputs | change fee | remainder exists", | ||
"feePerKb": 1000, | ||
"unspents": [7000, 5000, 5000, 5000, 5000, 5000], | ||
"outputs": [28000, 1000], | ||
"expected": { | ||
"fee": 2000, | ||
"inputs": [0, 1, 2, 3, 4, 5], | ||
"remainder": 1000 | ||
} | ||
}, | ||
{ | ||
"description": "no outputs | remainder expected", | ||
"feePerKb": 10000, | ||
"outputs": [], | ||
"unspents": [30000, 16000, 10000], | ||
"expected": { | ||
"fee": 10000, | ||
"inputs": [0], | ||
"remainder": 20000 | ||
} | ||
}, | ||
{ | ||
"description": "no solution | target too high (40000 > 20000)", | ||
"feePerKb": 10000, | ||
"outputs": [40000], | ||
"unspents": [20000] | ||
}, | ||
{ | ||
"description": "no solution | target too high (w/ fee) (40000 + fee > 40000)", | ||
"feePerKb": 10000, | ||
"outputs": [40000], | ||
"unspents": [40000] | ||
} | ||
] |
@@ -9,3 +9,3 @@ /* global beforeEach, describe, it */ | ||
describe('coinSelect', function () { | ||
fixtures.valid.forEach(function (f) { | ||
fixtures.forEach(function (f) { | ||
var outputs, unspents | ||
@@ -21,2 +21,5 @@ | ||
// ensure a solution was found | ||
if (!f.expected) return assert.equal(result, undefined) | ||
// ensure remainder is correctly calculated | ||
@@ -37,17 +40,2 @@ assert.equal(result.remainder, f.expected.remainder, 'Invalid remainder: ' + result.remainder + ' !== ' + f.expected.remainder) | ||
}) | ||
fixtures.invalid.forEach(function (f) { | ||
var outputs, unspents | ||
beforeEach(function () { | ||
outputs = f.outputs.map(function (value) { return { value: value } }) | ||
unspents = f.unspents.map(function (value) { return { value: value } }) | ||
}) | ||
it('throws on ' + f.exception, function () { | ||
assert.throws(function () { | ||
coinSelect(unspents, outputs, f.feePerKb) | ||
}, new RegExp(f.exception)) | ||
}) | ||
}) | ||
}) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
9273
205