Skip to content

Commit

Permalink
Convert to BN
Browse files Browse the repository at this point in the history
  • Loading branch information
rob-cb committed Apr 17, 2019
1 parent b3c487a commit b59a073
Show file tree
Hide file tree
Showing 23 changed files with 5,008 additions and 1,892 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
coverage
.nyc_output
node_modules
.vscode/
32 changes: 21 additions & 11 deletions accumulative.js
Original file line number Diff line number Diff line change
@@ -1,38 +1,48 @@
var utils = require('./utils')
var ext = require('./bn-extensions')

// add inputs until we reach or surpass the target value (or deplete)
// worst-case: O(n)
module.exports = function accumulative (utxos, outputs, feeRate) {
if (!isFinite(utils.uintOrNaN(feeRate))) return {}
if (!isFinite(utils.bnOrNaN(feeRate))) return {}
var bytesAccum = utils.transactionBytes([], outputs)

var inAccum = 0
var inAccum = ext.BN_ZERO
var inputs = []
var outAccum = utils.sumOrNaN(outputs)

for (var i = 0; i < utxos.length; ++i) {
var utxo = utxos[i]
var utxoBytes = utils.inputBytes(utxo)
var utxoFee = feeRate * utxoBytes
var utxoValue = utils.uintOrNaN(utxo.value)
var utxoFee = ext.mul(feeRate, utxoBytes)
var utxoValue = utils.bnOrNaN(utxo.value)

// skip detrimental input
if (utxoFee > utxo.value) {
if (i === utxos.length - 1) return { fee: feeRate * (bytesAccum + utxoBytes) }
var feeIsMoreThanValue = ext.gt(utxoFee, utxoValue)
// utxoFee > utxoValue
if (feeIsMoreThanValue) {
if (i === utxos.length - 1) {
var bytesSum = ext.add(bytesAccum, utxoBytes)
return {
fee: ext.mul(feeRate, bytesSum)
}
}
continue
}

bytesAccum += utxoBytes
inAccum += utxoValue
bytesAccum = ext.add(bytesAccum, utxoBytes)
inAccum = ext.add(inAccum, utxoValue)
inputs.push(utxo)

var fee = feeRate * bytesAccum
var fee = ext.mul(feeRate, bytesAccum)

// go again?
if (inAccum < outAccum + fee) continue
if (ext.lt(inAccum, ext.add(outAccum, fee))) continue

return utils.finalize(inputs, outputs, feeRate)
}

return { fee: feeRate * bytesAccum }
return {
fee: feeRate.mul(bytesAccum)
}
}
27 changes: 17 additions & 10 deletions blackjack.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,42 @@
var utils = require('./utils')
var ext = require('./bn-extensions')

// only add inputs if they don't bust the target value (aka, exact match)
// worst-case: O(n)
module.exports = function blackjack (utxos, outputs, feeRate) {
if (!isFinite(utils.uintOrNaN(feeRate))) return {}
if (!isFinite(utils.bnOrNaN(feeRate))) return {}

var bytesAccum = utils.transactionBytes([], outputs)

var inAccum = 0
var inAccum = ext.BN_ZERO
var inputs = []
var outAccum = utils.sumOrNaN(outputs)
var threshold = utils.dustThreshold({}, feeRate)

for (var i = 0; i < utxos.length; ++i) {
var input = utxos[i]
var inputBytes = utils.inputBytes(input)
var fee = feeRate * (bytesAccum + inputBytes)
var inputValue = utils.uintOrNaN(input.value)
var fee = ext.mul(feeRate, ext.add(bytesAccum, inputBytes))
var inputValue = utils.bnOrNaN(input.value)

// would it waste value?
if ((inAccum + inputValue) > (outAccum + fee + threshold)) continue
var totalInputs = ext.add(inAccum, inputValue)
var outputsAndFee = ext.add(outAccum, fee)
var totalOutputs = ext.add(outputsAndFee, threshold)
var inputsAreGreaterThanOutputs = ext.gt(totalInputs, totalOutputs)

bytesAccum += inputBytes
inAccum += inputValue
if (inputsAreGreaterThanOutputs) continue

bytesAccum = ext.add(bytesAccum, inputBytes)
inAccum = ext.add(inAccum, inputValue)
inputs.push(input)

// go again?
if (inAccum < outAccum + fee) continue
if (ext.lt(inAccum, outputsAndFee)) continue

return utils.finalize(inputs, outputs, feeRate)
}

return { fee: feeRate * bytesAccum }
return {
fee: feeRate.mul(bytesAccum)
}
}
74 changes: 74 additions & 0 deletions bn-extensions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
var BN = require('bn.js')

var BN_ZERO = new BN(0)
var BN_ONE = new BN(1)

function mul (multiplicand, multiplier) {
if (!BN.isBN(multiplicand) || !BN.isBN(multiplier)) return NaN

return multiplicand.mul(multiplier)
}

function div (dividend, divisor) {
if (!BN.isBN(dividend) || !BN.isBN(divisor)) return NaN
if (divisor.cmp(BN_ZERO) === 0) return Infinity

return dividend.div(divisor)
}

function add (arg1, arg2, arg3) {
// Add two items
if (!BN.isBN(arg1) || !BN.isBN(arg2)) return NaN
if (typeof arg3 === 'undefined') return arg1.add(arg2)

// Add three items
if (!BN.isBN(arg3)) return NaN
return arg1.add(arg2).add(arg3)
}

function sub (arg1, arg2, arg3) {
// Subtract two items
if (!BN.isBN(arg1) || !BN.isBN(arg2)) return NaN
if (typeof arg3 === 'undefined') return arg1.sub(arg2)

// Subtract three items
if (!BN.isBN(arg3)) return NaN
return arg1.sub(arg2).sub(arg3)
}

function shrn (argument, shiftBy) {
if (!BN.isBN(argument)) return NaN
if (BN.isBN(shiftBy)) shiftBy = shiftBy.toNumber()
if (typeof shiftBy !== 'number') return NaN

return argument.shrn(shiftBy)
}

function isZero (argument) {
if (!BN.isBN(argument)) return false
if (argument.cmp(BN_ZERO) === 0) return true
return false
}

function lt (subject, argument) {
if (!BN.isBN(argument) || !BN.isBN(subject)) return false
return subject.lt(argument)
}

function gt (subject, argument) {
if (!BN.isBN(argument) || !BN.isBN(subject)) return false
return subject.gt(argument)
}

module.exports = {
mul: mul,
div: div,
add: add,
sub: sub,
shrn: shrn,
isZero: isZero,
lt: lt,
gt: gt,
BN_ZERO: BN_ZERO,
BN_ONE: BN_ONE
}
27 changes: 17 additions & 10 deletions break.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,39 @@
var utils = require('./utils')
var ext = require('./bn-extensions')

// break utxos into the maximum number of 'output' possible
module.exports = function broken (utxos, output, feeRate) {
if (!isFinite(utils.uintOrNaN(feeRate))) return {}
if (!isFinite(utils.bnOrNaN(feeRate))) return {}

var bytesAccum = utils.transactionBytes(utxos, [])
var value = utils.uintOrNaN(output.value)
var value = utils.bnOrNaN(output.value)
var inAccum = utils.sumOrNaN(utxos)

if (!isFinite(value) ||
!isFinite(inAccum)) return { fee: feeRate * bytesAccum }
!isFinite(inAccum)) return { fee: feeRate.mul(bytesAccum) }

var outputBytes = utils.outputBytes(output)
var outAccum = 0
var outAccum = ext.BN_ZERO
var outputs = []

while (true) {
var fee = feeRate * (bytesAccum + outputBytes)
// feeRate * (bytesAccum + outputBytes)
var fee = ext.mul(feeRate, ext.add(bytesAccum, outputBytes))

// did we bust?
if (inAccum < (outAccum + fee + value)) {
if (ext.lt(inAccum, ext.add(outAccum, fee, value))) {
var isZero = ext.isZero(outAccum)
// premature?
if (outAccum === 0) return { fee: fee }

if (isZero) {
return {
fee: fee
}
}
break
}

bytesAccum += outputBytes
outAccum += value
bytesAccum = ext.add(bytesAccum, outputBytes)
outAccum = ext.add(outAccum, value)
outputs.push(output)
}

Expand Down
4 changes: 2 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ var utils = require('./utils')

// order by descending value, minus the inputs approximate fee
function utxoScore (x, feeRate) {
return x.value - (feeRate * utils.inputBytes(x))
return x.value.sub((feeRate.mul(utils.inputBytes(x))))
}

module.exports = function coinSelect (utxos, outputs, feeRate) {
utxos = utxos.concat().sort(function (a, b) {
return utxoScore(b, feeRate) - utxoScore(a, feeRate)
return utxoScore(b, feeRate).sub(utxoScore(a, feeRate))
})

// attempt to use the blackjack strategy first (no change output)
Expand Down
Loading

0 comments on commit b59a073

Please sign in to comment.