coinselectsyscoin
Version:
A transaction input selection module for syscoin.
117 lines (101 loc) • 3.14 kB
JavaScript
const BN = require('bn.js')
const ext = require('./bn-extensions')
const SYSCOIN_TX_VERSION_SYSCOIN_BURN_TO_ALLOCATION = 139
const SYSCOIN_TX_VERSION_ALLOCATION_MINT = 140
function isNonAssetFunded (txVersion) {
return txVersion === SYSCOIN_TX_VERSION_SYSCOIN_BURN_TO_ALLOCATION || txVersion === SYSCOIN_TX_VERSION_ALLOCATION_MINT
}
// baseline estimates, used to improve performance
const TX_BASE_SIZE = new BN(11)
const TX_INPUT_SIZE = {
LEGACY: new BN(147),
P2SH: new BN(91),
BECH32: new BN(68)
}
const TX_OUTPUT_SIZE = {
LEGACY: new BN(34),
P2SH: new BN(32),
BECH32: new BN(31)
}
function inputBytes (input) {
return TX_INPUT_SIZE[input.type] || TX_INPUT_SIZE.LEGACY
}
function outputBytes (output) {
if (output.script) {
return new BN(output.script.length + 5 + 8) // 5 for OP_PUSHDATA2 max OP_RETURN prefix, 8 for amount
}
return TX_OUTPUT_SIZE[output.type] || TX_OUTPUT_SIZE.LEGACY
}
function dustThreshold (output, feeRate) {
/* ... classify the output for input estimate */
return ext.mul(inputBytes(output), feeRate)
}
function transactionBytes (inputs, outputs) {
return TX_BASE_SIZE
.add(inputs.reduce(function (a, x) {
return ext.add(a, inputBytes(x))
}, ext.BN_ZERO))
.add(outputs.reduce(function (a, x) {
return ext.add(a, outputBytes(x))
}, ext.BN_ZERO))
}
function uintOrNull (v) {
if (!BN.isBN(v)) return null
if (v.isNeg()) return null
return v
}
function sumForgiving (range) {
return range.reduce(function (a, x) {
const valueOrZero = BN.isBN(x.value) ? x.value : ext.BN_ZERO
return ext.add(a, valueOrZero)
},
ext.BN_ZERO)
}
function sumOrNaN (range) {
return range.reduce(function (a, x) {
const value = x.value
return ext.add(a, uintOrNull(value))
}, ext.BN_ZERO)
}
function finalize (inputs, outputs, feeRate, feeBytes, txVersion) {
const bytesAccum = transactionBytes(inputs, outputs)
const feeAfterExtraOutput = ext.mul(feeRate, ext.add(bytesAccum, feeBytes))
const remainderAfterExtraOutput = ext.sub(sumOrNaN(inputs), ext.add(sumOrNaN(outputs, txVersion), feeAfterExtraOutput))
// is it worth a change output?
if (ext.gt(remainderAfterExtraOutput, dustThreshold({}, feeRate))) {
outputs = outputs.concat({ changeIndex: outputs.length, value: remainderAfterExtraOutput })
}
const fee = ext.sub(sumOrNaN(inputs), sumOrNaN(outputs, txVersion))
if (!fee) return { fee: ext.mul(feeRate, bytesAccum) }
return {
inputs: inputs,
outputs: outputs,
fee: fee
}
}
function finalizeAssets (inputs, outputs, assetAllocations) {
if (!inputs || !outputs || !assetAllocations) {
return {
inputs: null,
outputs: null,
assetAllocations: null
}
}
return {
inputs: inputs,
outputs: outputs,
assetAllocations: assetAllocations
}
}
module.exports = {
dustThreshold: dustThreshold,
finalize: finalize,
finalizeAssets: finalizeAssets,
inputBytes: inputBytes,
outputBytes: outputBytes,
sumOrNaN: sumOrNaN,
sumForgiving: sumForgiving,
transactionBytes: transactionBytes,
uintOrNull: uintOrNull,
isNonAssetFunded: isNonAssetFunded
}