coinselect
Advanced tools
Comparing version 2.0.1 to 3.0.0
106
index.js
@@ -1,74 +0,42 @@ | ||
// TODO: integrate privacy calculations, group by address, avoid linking multiple addresses together | ||
let accumulative = require('./accum') | ||
let blackjack = require('./blackjack') | ||
// XXX: these are based on pubKeyHash estimates, used to improve performance | ||
var TX_EMPTY_SIZE = 8 | ||
var TX_INPUT_BASE = 40 + 2 | ||
var TX_INPUT_PUBKEYHASH = 106 | ||
var TX_OUTPUT_BASE = 8 + 1 | ||
var TX_OUTPUT_PUBKEYHASH = 25 | ||
// TODO | ||
// function groupByRelation (utxos) { | ||
// let txoMap = {} | ||
// let result = [] | ||
// | ||
// // group by address/script | ||
// utxos.forEach((utxo) => { | ||
// let key = utxo.address || utxo.script | ||
// | ||
// // no relation known, use as is | ||
// if (!key) return result.push(utxo) | ||
// | ||
// // else, append relation via key | ||
// if (!txoMap[key]) txoMap[key] = [] | ||
// txoMap[key].push(utxo) | ||
// }) | ||
// | ||
// for (let key in txoMap) { | ||
// let group = txoMap[key] | ||
// | ||
// // summate 'grouping' value | ||
// group.value = group.reduce((a, x) => a + x.value, 0) | ||
// result.push(group) | ||
// } | ||
// | ||
// return result | ||
// } | ||
function estimateRelayFee (byteLength, feeRate) { | ||
return byteLength * feeRate | ||
} | ||
module.exports = function coinSelect (utxos, outputs, feeRate) { | ||
// order by descending value | ||
utxos = utxos.concat().sort((a, b) => b.value - a.value) | ||
module.exports = function coinSelect (unspents, outputs, feeRate) { | ||
// sort by descending value | ||
var sorted = unspents.concat().sort(function (o1, o2) { | ||
return o2.value - o1.value | ||
}) | ||
// attempt to use the blackjack strategy first (no change output) | ||
let base = blackjack(utxos, outputs, feeRate) | ||
if (base.inputs) return base | ||
var byteLength = TX_EMPTY_SIZE | ||
var target = 0 | ||
outputs.forEach(function (output) { | ||
byteLength += TX_OUTPUT_BASE + (output.script ? output.script.length : TX_OUTPUT_PUBKEYHASH) | ||
target += output.value | ||
}) | ||
var accum = 0 | ||
for (var i = 0; i < sorted.length; ++i) { | ||
var unspent = sorted[i] | ||
// TODO: an estimate is used because of missing signature data | ||
byteLength += TX_INPUT_BASE + TX_INPUT_PUBKEYHASH | ||
accum += unspent.value | ||
// ignore fees until we have the minimum amount | ||
if (accum < target) continue | ||
var baseFee = estimateRelayFee(byteLength, feeRate) | ||
var total = target + baseFee | ||
// continue until we can afford the base fee | ||
if (accum < total) continue | ||
var inputs = sorted.slice(0, i + 1) | ||
var feeWithChange = estimateRelayFee(byteLength + TX_OUTPUT_BASE + TX_OUTPUT_PUBKEYHASH, feeRate) | ||
var totalWithChange = target + feeWithChange | ||
// can we afford a change output? | ||
if (accum >= totalWithChange) { | ||
var remainderWithChange = accum - totalWithChange | ||
return { | ||
fee: feeWithChange, | ||
inputs: inputs, | ||
remainder: remainderWithChange | ||
} | ||
} | ||
var remainder = accum - total | ||
return { | ||
fee: baseFee + remainder, | ||
inputs: inputs, | ||
remainder: 0 | ||
} | ||
} | ||
return { | ||
fee: estimateRelayFee(byteLength, feeRate), | ||
inputs: null | ||
} | ||
// else, try the accumulative strategy | ||
return accumulative(utxos, outputs, feeRate) | ||
} |
{ | ||
"name": "coinselect", | ||
"version": "2.0.1", | ||
"description": "A fee optimizing bitcoin input selection module", | ||
"version": "3.0.0", | ||
"description": "A transaction input selection module for bitcoin.", | ||
"keywords": [ | ||
@@ -26,3 +26,6 @@ "coinselect", | ||
"files": [ | ||
"index.js" | ||
"accumulative.js", | ||
"blackjack.js", | ||
"index.js", | ||
"utils.js" | ||
], | ||
@@ -44,4 +47,6 @@ "main": "index.js", | ||
"standard": "*", | ||
"tap-spec": "^4.1.1", | ||
"tape": "^4.5.1" | ||
} | ||
}, | ||
"dependencies": {} | ||
} |
@@ -8,7 +8,7 @@ # coinselect | ||
A fee-optimizing, transaction input selection module for bitcoinjs-lib. | ||
A transaction input selection module for bitcoin. | ||
The code is stable. | ||
The module's interface/existence is not. | ||
The module's interface is not. | ||
@@ -21,8 +21,9 @@ Please let me know if you are using this package. | ||
``` javascript | ||
var coinSelect = require('coinselect') | ||
var feeRate = 55 // satoshis per byte | ||
var unspents = [ | ||
let coinSelect = require('coinselect') | ||
let feeRate = 55 // satoshis per byte | ||
let utxos = [ | ||
..., | ||
{ | ||
txId: '...', | ||
vout: 0, | ||
..., | ||
@@ -32,3 +33,3 @@ value: 10000 | ||
] | ||
var outputs = [ | ||
let targets = [ | ||
..., | ||
@@ -41,17 +42,23 @@ { | ||
var result = coinselect(unspents, outputs, feeRate) | ||
// ... | ||
let { inputs, outputs, fee } = coinSelect(utxos, targets, feeRate) | ||
// the accumulated fee is always returned | ||
console.log(result.fee) | ||
// the accumulated fee is always returned for analysis | ||
console.log(fee) | ||
// .inputs may be null if not enough funds exist | ||
if (!result.inputs) return | ||
// .inputs and .outputs will be undefined if no solution was found | ||
if (!inputs || !outputs) return | ||
// success! | ||
var txb = new bitcoin.TransactionBuilder() | ||
let txb = new bitcoin.TransactionBuilder() | ||
// is a change output non-dust? | ||
if (result.remainder > 5460) { | ||
txb.addOutput(changeAddress, result.remainder) | ||
} | ||
inputs.forEach(input => txb.addInput(input.txId, input.vout)) | ||
outputs.forEach(output => { | ||
// watch out, outputs may have been added that you need to provide | ||
// an output address/script for | ||
if (!output.address) { | ||
output.address = wallet.getChangeAddress() | ||
} | ||
txb.addOutput(output.address, output.value) | ||
}) | ||
``` | ||
@@ -58,0 +65,0 @@ |
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
6475
6
91
66
4