Giter Club home page Giter Club logo

coinselect's Introduction

coinselect

TRAVIS NPM

js-standard-style

An unspent transaction output (UTXO) selection module for bitcoin.

WARNING: Value units are in satoshis, not Bitcoin.

Algorithms

Module Algorithm Re-orders UTXOs?
require('coinselect') Blackjack, with Accumulative fallback By Descending Value
require('coinselect/accumulative') Accumulative - accumulates inputs until the target value (+fees) is reached, skipping detrimental inputs -
require('coinselect/blackjack') Blackjack - accumulates inputs until the target value (+fees) is matched, does not accumulate inputs that go over the target value (within a threshold) -
require('coinselect/break') Break - breaks the input values into equal denominations of output (as provided) -
require('coinselect/split') Split - splits the input values evenly between all outputs, any provided output with .value remains unchanged -

Note: Each algorithm will add a change output if the input - output - fee value difference is over a dust threshold. This is calculated independently by utils.finalize, irrespective of the algorithm chosen, for the purposes of safety.

Pro-tip: if you want to send-all inputs to an output address, coinselect/split with a partial output (.address defined, no .value) can be used to send-all, while leaving an appropriate amount for the fee.

Example

let coinSelect = require('coinselect')
let feeRate = 55 // satoshis per byte
let utxos = [
  ...,
  {
    txId: '...',
    vout: 0,
    ...,
    value: 10000,
    // For use with PSBT:
    // not needed for coinSelect, but will be passed on to inputs later
    nonWitnessUtxo: Buffer.from('...full raw hex of txId tx...', 'hex'),
    // OR
    // if your utxo is a segwit output, you can use witnessUtxo instead
    witnessUtxo: {
      script: Buffer.from('... scriptPubkey hex...', 'hex'),
      value: 10000 // 0.0001 BTC and is the exact same as the value above
    }
  }
]
let targets = [
  ...,
  {
    address: '1EHNa6Q4Jz2uvNExL497mE43ikXhwF6kZm',
    value: 5000
  }
]

// ...
let { inputs, outputs, fee } = coinSelect(utxos, targets, feeRate)

// the accumulated fee is always returned for analysis
console.log(fee)

// .inputs and .outputs will be undefined if no solution was found
if (!inputs || !outputs) return

let psbt = new bitcoin.Psbt()

inputs.forEach(input =>
  psbt.addInput({
    hash: input.txId,
    index: input.vout,
    nonWitnessUtxo: input.nonWitnessUtxo,
    // OR (not both)
    witnessUtxo: input.witnessUtxo,
  })
)
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()
    wallet.nextChangeAddress()
  }

  psbt.addOutput({
    address: output.address,
    value: output.value,
  })
})

License MIT

coinselect's People

Contributors

chrischo-h avatar dcousens avatar dependabot[bot] avatar junderw avatar kaladinlight avatar karelbilek avatar missmonacoin avatar overtorment avatar tiero avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

coinselect's Issues

PR #49 + npm

Can confirm that #49 is also is a fix for if you use the "pro tip" (to sweep all inputs to an address) with a large total input balance (for example 10000000000)..

Managed to debug it then found out that npm was out of date and it was fixed already in the repo!

can npm be updated? thanks

p2sh(p2spkh) to p2sh(p2wsh)multi transaction error when using coinselect

I've 'manually' tested a p2sh(p2wpkh) to p2sh(p2wsh)multi transaction push to an api which worked fine. The transaction can be seen on this multi address (should be the first of not only): 2NAYnkhAQ6ctSfsXf37McRo2T6ShUayEBhc

I then tried a slightly more complex transaction to the same address using coinselect which suppled multiple inputs and outputs as expected. The transaction decoded also looks good but I'm getting the following stack error and some apis are saying there are no inputs at all:

Possibly unhandled Error: Validation Error: BitcoindException(super=com.neemre.btcdcli4j.core.BitcoindException: Error #-26: 18: txn-mempool-conflict, code=-26) at Object.ensureErrorObject (/Users/peter/Desktop/MSC_Project_writeup/script_files/back-end/node_modules/request-promise/node_modules/bluebird/js/main/util.js:261:20) at Promise._rejectCallback (/Users/peter/Desktop/MSC_Project_writeup/script_files/back-end/node_modules/request-promise/node_modules/bluebird/js/main/promise.js:472:22) at Promise._settlePromiseFromHandler (/Users/peter/Desktop/MSC_Project_writeup/script_files/back-end/node_modules/request-promise/node_modules/bluebird/js/main/promise.js:516:17) at Promise._settlePromiseAt (/Users/peter/Desktop/MSC_Project_writeup/script_files/back-end/node_modules/request-promise/node_modules/bluebird/js/main/promise.js:584:18) at Promise._settlePromises (/Users/peter/Desktop/MSC_Project_writeup/script_files/back-end/node_modules/request-promise/node_modules/bluebird/js/main/promise.js:700:14) at Async._drainQueue (/Users/peter/Desktop/MSC_Project_writeup/script_files/back-end/node_modules/request-promise/node_modules/bluebird/js/main/async.js:123:16) at Async._drainQueues (/Users/peter/Desktop/MSC_Project_writeup/script_files/back-end/node_modules/request-promise/node_modules/bluebird/js/main/async.js:133:10) at Immediate.Async.drainQueues (/Users/peter/Desktop/MSC_Project_writeup/script_files/back-end/node_modules/request-promise/node_modules/bluebird/js/main/async.js:15:14) at runCallback (timers.js:810:20) at tryOnImmediate (timers.js:768:5) at processImmediate [as _immediateCallback] (timers.js:745:5)
My hex is:

020000000001028fc38732d0b82585cdf4c75217609aaca03e2cc20f9ed9bed7d09491745fab7100000000171600143a7efe8788baac9747ba259e95987bdaf12bd469ffffffff69611aca749c8ff67dc425ca45e6b030741c41aa61eed9251fa429ee402e6b8101000000171600143a7efe8788baac9747ba259e95987bdaf12bd469ffffffff0250bb25010000000017a914bdcc8fa3456a212caa2c3b7ef8d0f8b24841a5e187d0cd4d000000000017a91436f6eb8e1f4d67d02aca3c96b334a320fda4bc328702483045022100d5d1147eb35d4ada7b8435b4b8088174fd45cb89cafcb5ccb36030c99f42228f022067827ec3f9f40ccc93897bf7f1ec95398995071af8fadb2d869511a68b3626700121036d3f74d39e6d4cb0695b99e134a207be9f980c0e4e24931a0be1403a40891da70247304402200f1b91f65b27f97c1817b62a18041caae95c85fc0e3ed1d72dc834d358f7dc2702206a0a3f7a833008cf8abaadc006f9de630b151cd12c7b6ff160be1802f3eb86e90121036d3f74d39e6d4cb0695b99e134a207be9f980c0e4e24931a0be1403a40891da700000000

My code is (the p2sh(p2wsh)multi process is below this):

function rng() { return Buffer.from('zzzzzzzzzzzzzzzzzwwwzzzzzzzzzzzz') } // sender
const keyPair_New = bitcoin.ECPair.makeRandom({ rng: rng, network: testnet })
// //2MxFrGxKtuqAYy8ZEsbBpD73L6eEz67Rrjp

const p2wpkh = bitcoin.payments.p2wpkh({ pubkey: keyPair_New.publicKey, network: testnet })
const p2sh = bitcoin.payments.p2sh({ redeem: p2wpkh, network: testnet })

let feeRate = 70 // satoshis per byte from https://bitcoinfees.earn.com/api
let utxos = [{
        address: '2MxFrGxKtuqAYy8ZEsbBpD73L6eEz67Rrjp',
        txid: '01b106defc8c785676f4bdac194a49fb55a908146a660f0d9841b13e32dee5d3',
        vout: 0,
        scriptPubKey: 'a91436f6eb8e1f4d67d02aca3c96b334a320fda4bc3287',
        amount: 0.0203125,
        satoshis: 2031250,
        confirmations: 0,
        ts: 1534008290
    },
    {
        address: '2MxFrGxKtuqAYy8ZEsbBpD73L6eEz67Rrjp',
        txid: '085abb08e50c4225a282dbdccea778dd29c57ee46f0a0d49357e53381b8a42eb',
        vout: 1,
        scriptPubKey: 'a91436f6eb8e1f4d67d02aca3c96b334a320fda4bc3287',
        amount: 0.040625,
        satoshis: 4062500,
        confirmations: 0,
        ts: 1534008276
    },
    {
        address: '2MxFrGxKtuqAYy8ZEsbBpD73L6eEz67Rrjp',
        txid: '816b2e40ee29a41f25d9ee61aa411c7430b0e645ca25c47df68f9c74ca1a6169',
        vout: 1,
        scriptPubKey: 'a91436f6eb8e1f4d67d02aca3c96b334a320fda4bc3287',
        amount: 0.08125,
        satoshis: 8125000,
        confirmations: 0,
        ts: 1534008264
    },
    {
        address: '2MxFrGxKtuqAYy8ZEsbBpD73L6eEz67Rrjp',
        txid: '71ab5f749194d0d7bed99e0fc22c3ea0ac9a601752c7f4cd8525b8d03287c38f',
        vout: 0,
        scriptPubKey: 'a91436f6eb8e1f4d67d02aca3c96b334a320fda4bc3287',
        amount: 0.1625,
        satoshis: 16250000,
        confirmations: 0,
        ts: 1534007890
    }
]
let targets = [{
    address: '2NAYnkhAQ6ctSfsXf37McRo2T6ShUayEBhc',
    satoshis: 19250000

}]

let { inputs, outputs, fee } = coinSelect(utxos, targets, feeRate)

if (!inputs || !outputs) return

let txb = new bitcoin.TransactionBuilder(testnet)

inputs.forEach(input => txb.addInput(input.txid, input.vout))

 outputs.forEach(output => {
     if (!output.address) {
         output.address = '2MxFrGxKtuqAYy8ZEsbBpD73L6eEz67Rrjp'
     }
     txb.addOutput(output.address, output.satoshis)
     console.log(output.address, output.satoshis)
 }) 

var n = inputs.length;
 for (var i = 0; i < n ; i++) {
     console.log(i)
      txb.sign(i, keyPair_New, p2sh.redeem.output, null, inputs[i].satoshis)
 }
const tx = txb.build()
const txt = tx.toHex()
pushtx.pushtx(txt)

// ==================
// create multi address (not part of the above script):

function gnr() { return Buffer.from('zzzzzzzzzzzzzzzzzyyyzzzzzzzzzzzz') } // signer
const keyPairR = bitcoin.ECPair.makeRandom({ rng: gnr, network: testnet })

function rng() { return Buffer.from('zzzzzzzzzzzzzzzzzxxxzzzzzzzzzzzz') } // co-signer 
const keyPairCR = bitcoin.ECPair.makeRandom({ rng: rng, network: testnet })

const pubkeys = [keyPairR.publicKey, keyPairCR.publicKey];
 const { address } = bitcoin.payments.p2sh({
     redeem: bitcoin.payments.p2wsh({
         redeem: bitcoin.payments.p2ms({ m: 2, pubkeys, network: bitcoin.networks.testnet }),
         network: bitcoin.networks.testnet
     }), network: bitcoin.networks.testnet
})
// save address for creating transaction 

Doubt about feeRate unit

  1. Is the feeRate must be positive integer ?
function uintOrNaN (v) {
  if (typeof v !== 'number') return NaN
  if (!isFinite(v)) return NaN
  if (Math.floor(v) !== v) return NaN
  if (v < 0) return NaN
  return v
}

uintOrNaN

  1. The rpc of estimatesmartfee result, fee rate in BTC/kB, So the feeRate unit would be better satoshis per kB than satoshis per byte ?

What is the best way to handle user selection of virtual bytes in coinselect?

For users wallets I want to allow them to choose a desired satoshi/vB level, depending on how fast they wish to have their transactions confirmed.

However, I see that coinselect only uses feeRate in satoshi/byte, which is an independent metric.

What's the best practices for performing coin-selection with desired satoshi/vB? Should I just use an arbitrary feeRate (1 or 0 perhaps), and then make adjustments myself to the change address value afterwards?

Thanks

fees can be <1 sat/B but bigger than 0 sat/B

I am not sure how well is the code handling fees smaller than 1sat/B

For example DASH regularly has those fees and there is no reason why bitcoin couldn't have that

(yes, I know code in this repo is old and I sort of gave up on helping you with it :( but I noticed this issue now )

Order utxos by age

Coinselection should prefer older utxos and use unconfirmed utxos the latest

No inputs or outputs generated

I'd add a label "Question / Help Wanted" to this issue but I can't figure out how to do it. I'm working on the TESTNET and with the following query:

curl https://api.blockcypher.com/v1/btc/test3/addrs/mjGtZWXgPdbLUZYWtDqVTfbFieVzXFfSEj?unspendOnly=true

I get the following Json:

{
  "address": "mjGtZWXgPdbLUZYWtDqVTfbFieVzXFfSEj",
  "total_received": 10631,
  "total_sent": 0,
  "balance": 10631,
  "unconfirmed_balance": 0,
  "final_balance": 10631,
  "n_tx": 2,
  "unconfirmed_n_tx": 0,
  "final_n_tx": 2,
  "txrefs": [
    {
      "tx_hash": "6861bc1cb3564c907343a6dc83651827f9da1346e36417ce6b46a5753d32a033",
      "block_height": 1657954,
      "tx_input_n": -1,
      "tx_output_n": 0,
      "value": 8336,
      "ref_balance": 10631,
      "spent": false,
      "confirmations": 10,
      "confirmed": "2020-01-10T04:27:39Z",
      "double_spend": false
    },
    {
      "tx_hash": "e6a1fb15c1480050179b8dc6325dfacfb35c869b2f8f0397e64625b0e48b9b2c",
      "block_height": 1657484,
      "tx_input_n": -1,
      "tx_output_n": 0,
      "value": 2295,
      "ref_balance": 2295,
      "spent": false,
      "confirmations": 480,
      "confirmed": "2020-01-09T22:16:00Z",
      "double_spend": false
    }
  ],
  "tx_url": "https://api.blockcypher.com/v1/btc/test3/txs/"
}

which I digest like this:

const CoinSelect = require('coinselect');
var utxos = o.txrefs.map(o => ({
    txId: o.tx_hash, vout: parseInt(o.tx_output_n), value: parseInt(o.value)
}));
var targets = [{address: dst, value}];
var ret = CoinSelect(utxos, targets, feeRate);

such that what I feed the function looks like this (utxos):

[ { txId:
     '6861bc1cb3564c907343a6dc83651827f9da1346e36417ce6b46a5753d32a033',
    vout: 0,
    value: 8336 },
  { txId:
     'e6a1fb15c1480050179b8dc6325dfacfb35c869b2f8f0397e64625b0e48b9b2c',
    vout: 0,
    value: 2295 } ]

and targets:

[ { address: '2N7nSZjfqikj4oVkCqaLB3CNMY3TA72PbNY', value: 500 } ]

however, what the function returns to me is:

{ fee: 18590 }

which means something is failing. I can't figure it out. is it obvious to anyone what it might be?

Rationale for the blackjack strategy

What is the rationale for using blackjack strategy, for selecting the exact match?

It might create a lower fee, but will it create longer fees long-term?

Consider Output Address Types

How does coinselect(utxos, targets, feeRate) get a correct transaction size when coinselect does not know what type of change address will be used? The change address could be a really large script that could push the fee rate below the required fee rate or make change not worth keeping. Great library and thanks so much for helping me clarify my understandings.

No solution found if many inputs

If there are many UTXOs being parsed into coinselect for a solution it does not appear to handle them and outputs no solution

CoinSelect using multiple small UTXOs instead of one big

Hi team, thanks for the great work.
I realized that sometimes coinselect (Blackjack, with Accumulative fallback) is using multiple smaller UTXOs rather than one big, and therefore ends up with higher fees when sending BTC. Isn't it the idea of the default module to select the biggest UTXO first? Am I maybe using a wrong one?

Inputs nor outputs are returned when a certain condition is met

If I understand this correctly, I shouldn't rely on the fee value returned unless either inputs or outputs are returned? Here is from the README example:

// .inputs and .outputs will be undefined if no solution was found
if (!inputs || !outputs) return

There is only a single UTXO provided to the coinselect and it runs to the following line, which makes it continue out of the loop and go to the bottom where only fee is returned:

if ((inAccum + inputValue) > (outAccum + fee + threshold)) continue

That results in both inputs and outputs being empty, which are only supplied if finalize is ever called, which in the case you have a single UTXO that has enough for output and fee.

return { fee: feeRate * bytesAccum }

Is this expected behavior, that inputs and outputs will be empty in a scenario like this and is the example misleading, or do I have other issues with the provided input data?

Reevaluating 'Dust' Considerations When Adding Change in Transactions

EDIT [Nov-19]: Upon further examination, it appears that Bitcoin Core indeed employs a similar approach. The main difference is that Core allows the user to set a custom dustRelayFee (default 3 sat/vbyte), which is independent of the feeRate. In contrast, the coinselect algorithm in this repository sets the dustRelayFee equal to the feeRate. This seems to be about optimizing blockchain efficiency, but I'm uncertain if the strategy of tying dustRelayFee directly to the current fee rates in this repo is the most effective approach. I'll keep this issue open for a while longer to welcome any additional feedback or insights.

This issue is raised for discussion, especially since I believe these algorithms are being utilized by various wallets (I am also working on similar ideas too).

I'd like to discuss a specific aspect of the coinselect algorithms, particularly concerning this line of code:

// is it worth a change output?

From a network efficiency perspective, avoiding the addition of Waste to transactions is beneficial, making this a good decision for network optimization (block space used).

However, when focusing on the user's interests, it's generally advantageous to receive change back, provided it aligns with the designated fee rate. Doing the opposite results in additional fees being ceded to miners. And in the future, should transaction fees decrease, these change UTXOs may become more viable for spending.

For example, look at the test fixtures, particularly this one:

"description": "inputs used in order of DESCENDING",

 {
    "description": "inputs used in order of DESCENDING",
    "feeRate": 10,
    "inputs": [
      20000,
      {
        "script": {
          "length": 300
        },
        "value": 10000
      },
      10000
    ],
    "outputs": [
      25000
    ],
    "expected": {
      "fee": 5000,
      "inputs": [
        {
          "i": 0,
          "value": 20000
        },
        {
          "i": 2,
          "value": 10000
        }
      ],
      "outputs": [
        {
          "value": 25000
        }
      ]
    }
  },

In this specific case, I believe the expected solution should include a change output. Assuming a P2PKH scenario, if you do the numbers, the change should have been 1260 sats.

A potential solution, if deemed appropriate, would be to bypass the Dust check when adding change.
A potential solution, if deemed appropriate, would be doing as Core:

  // is it worth a change output?
  if (remainderAfterExtraOutput > (148 + 34) * 3) {
    outputs = outputs.concat({ value: remainderAfterExtraOutput })
  }

I believe the criterion for Dust should primarily apply when deciding whether to add a new UTXO or not.

In terms of Dust, Bitcoin Core employs a specific policy for UTXOs: it avoids adding a UTXO if the value transferred is less than two-thirds of its total value a similar approach on new outputs.

You can find more details about this policy in their code:

https://github.com/bitcoin/bitcoin/blob/d752349029ec7a76f1fd440db2ec2e458d0f3c99/src/policy/policy.cpp#L26

As for the strategy concerning the addition of change outputs in transactions, especially in cases that might involve Dust, I have yet to investigate what Core does further.

I'm keen to hear thoughts on this issue.

"coinselect" always expose the largest UTXO no matter how small the payment is because it sorts by descending value.

This is a huge privacy issue.

It's exposing a larger balance than required to the recipient.

Why is it sorting by descending?

Unless it's doing something more advanced to solve that "knapsack' like problem.
It should first try to find the smallest single UTXO that is bigger than the sent amount by going up the list (asending)
If it didn't find any it should take the largest one and go back from to smallest until it finds one that is larger than what's missing. If not take 1st + 2nd largest and keep adding to it until total it's enough.

Inputs and outputs always undefined .

My testnet btc address has .53 btc and i am trying to select minimum utxos to send .01 btc to an address. but it always shows undefined inputs and outputs!

Subtracting fee from output amount

Hi, i mostly having a problem when i want to send "max amount from address"

Inputs from coinselect are always undefined because the fee is calculated on top of the target amount, and then I don't have enough satoshi in inputs to send "max target amount + fee"

To give an example, we have 100,000 satoshi on address, and we want to send all, so in target value we set 100,000 sat. and feerate 100 sat.
Fee is then calculated to be 24,000 sat, and by coinselect algorithm, i need 124,000 sat on the address to send 100,000.

What we need is that coinselect return output value of 76,000 and fee of 24,000. So when Transaction is built, 100% of satoshi is used, leaving an address with 0 balance.

Is this possible to do using coin select, or we need some kind of workaround for this

Thank you.

Proposal For Segwit Update PR

I would like to make a PR to update this library to default to segwit and other issues mentioned here. Most of the heavy work has been done in this PR.

I propose to build off this work and make 2 major changes to karel-3d's PR.

  1. Change Default Options to P2WSH Outputs and high R Sigs.
  2. Change coinSelect(utxos, targets, feeRate) to coinSelect(utxos, targets, feeRate, options)
    and allow developers to override the default settings with their specific cases.

Just looking for a Concept ACK before I start coding up the PR, I would hate to code the wrong thing. @junderw

Unsure how to use?

I can't seem to get this to work how I want it to. I want it to select inputs automatically. Can someone please help me. This is my code : createTransaction = (e) => { e.preventDefault() const {address, privateKey, add, amount} = this.state; var hashURL =https://api.blockcypher.com/v1/btc/test3/addrs/${address}`

        axios.get(hashURL).then((response) => {
            this.setState({
                hash1: response.data.txrefs[0].tx_hash,
                hash1Val: response.data.txrefs[0].value
            })
        }).then((result) => {


        var tx = new bitcoin.TransactionBuilder(testnet);

        var txId = this.state.hash;
        var txIdVal = this.state.hashVal;

        console.log(txId);

        tx.addInput(txId, 1)

        tx.addOutput(this.state.add, amount)
        //send change back and make fee = .001BTC
        tx.addOutput(this.state.address, hash1val - amount - 100000)

        var keyPair2 = bitcoin.ECPair.fromWIF(privateKey, testnet);

        tx.sign(0, keyPair2);
        console.log(tx.build().toHex());
        }
    )

Can someone please show me how to select inputs automatically? Thank you!`

Unable to spend money even with enough utxos.

I'm experiencing a problem where my total bitcoin balance is 1238944 satoshis.
Coin select is working perfectly to send 40000 satoshis or 20000 satoshis. It is unable to spend 30000 satoshis and returns undefined for the inputs and outputs. Here are the utxos:

[
    {
      "txId": "84e9748e60f4eeddf91b5c42c30f7c0a228bbcb6f72847ad62a3ce48bbcb5ea8",
      "vout": 0,
      "value": 100000
    },
    {
      "txId": "7f092d4d8b9d89db464b5fc9dfa2205bbb9f26a1f259da1d49139b0fc9a68297",
      "vout": 1,
      "value": 23000
    },
    {
      "txId": "3676d518060dccce621752153ea58ea659e40f6e40887166566b6ab3ab920032",
      "vout": 1,
      "value": 1000
    },
    {
      "txId": "127d54a7836486c3065dc4e26707976270c8f6b2d8b85f1629507288aba95f19",
      "vout": 0,
      "value": 10000
    },
    {
      "txId": "802b9507fcd4d13b204aa94d0e19e77669e925ef8f86b948af59faeb716c6f38",
      "vout": 0,
      "value": 100000
    },
    {
      "txId": "759bbe1b631207ac48342926c95d4b229eee04c6bf6c876e5f2ab52e25b60da8",
      "vout": 1,
      "value": 797319
    },
    {
      "txId": "4cd48916d574355b873f1db192df24dbcf6dcb4818a2317a873a9c95b55c4341",
      "vout": 1,
      "value": 207625
    }
  ]

value > 2^32 is not supported

I tried the following code:

const utxos = [
  {
    address: 'mqyirnfBTg9YfEbL1zY22LGB7QVJwuphZ5',
    amount: 5,
    confirmations: 147,
    scriptPubKey: '2102bce5d38cd72271ef353b77977b137ed10cd255aef4e290b1aae412a8060a1b9dac',
    solvable: true,
    spendable: true,
    txid: 'eef0b0e91b92cc35238434775d75b0ac6503f1bc2fe7dbcdf16272d588608f01',
    value: 5000000000,
    vout: 0
  },
  {
    address: 'mqyirnfBTg9YfEbL1zY22LGB7QVJwuphZ5',
    amount: 5,
    confirmations: 183,
    scriptPubKey: '2102bce5d38cd72271ef353b77977b137ed10cd255aef4e290b1aae412a8060a1b9dac',
    solvable: true,
    spendable: true,
    txid: 'a39c81513eeaa7dca6889f2f970a5b2263970d1669ee07f7910fa0eeaed70902',
    value: 5000000000,
    vout: 0
  }
]

const feeRate = 50
const targets = [{
  address: '1EHNa6Q4Jz2uvNExL497mE43ikXhwF6kZm',
  value: 1
}]

console.log(coinSelect(utxos, targets, feeRate))

But the result has no input or output, just the transaction fee:

 {fee: 9550}

Any idea what the problem is?

inputs and outputs returning unidefined when pending balance

Hi, everyone.
I'm in trouble to make bitcoin transactions.
After sending a transaction, the pending balance stays negative untill the transaction be confirmed.
But, if I try to send other transaction, the variables inputs and outputs returns undefined from coinselect.

My code is below:

  addressesWithUtxo = [],
  targetAddress,
  feeRate,
  changeAddress,
  mnemonic
) => {
  const utxos = await fetchUtxos(addressesWithUtxo);

  // If the are pending transaction, inputs and outputs returns as undefined
  let { inputs, outputs, fee } = coinSelect(utxos, [targetAddress], feeRate);

  console.log('Fee aquii => ', fee);

  var bitcoinNetwork = network;

  const seed = await bip39.mnemonicToSeed(mnemonic);
  const hdRoot = bitcoin.bip32.fromSeed(seed, bitcoinNetwork);

  // .inputs and .outputs will be undefined if no solution was found
  if (!inputs || !outputs) return;

  const psbt = new bitcoin.Psbt({ network });
  // { network: network }
  const derivationPath = "m/84'/0'/0'";

  for (const input of inputs) {
    const inputPublicKey = findPublicKeyForAddress(
      input.address,
      addressesWithUtxo
    );

    if (!inputPublicKey) {
      throw new Error('Could not find public key for input');
    }

    const p2sh = bitcoin.payments.p2sh({
      redeem: bitcoin.payments.p2wpkh({ pubkey: inputPublicKey })
    });

    psbt.addInput({
      hash: input.txid,
      index: input.vout,
      bip32Derivation: [
        {
          masterFingerprint: hdRoot.fingerprint,
          path: `${derivationPath}/${input.derivationPath}`,
          pubkey: inputPublicKey
        }
      ],
      witnessUtxo: {
        script: p2sh.output,
        value: input.value
      },
      redeemScript: p2sh.redeem.output
    });
  }

  for (const output of outputs) {
    if (!output.address) {
      output.address = changeAddress;
    }

    psbt.addOutput({
      address: output.address,
      value: output.value
    });

    console.log({
      address: output.address,
      value: output.value
    });
  }

  await psbt.signAllInputsHDAsync(hdRoot);

  // console.log(psbt.toBase64());

  const transaction = psbt.finalizeAllInputs().extractTransaction();

  // console.log('Transação => ', transaction);
  // console.log('Tohex => ', transaction.toHex());

  return { transaction, utxos, targetAddress, fee };
};

Segregated witness support should result in significantly different fees

Perhaps base it off a .witness field in the inputs... with which case:

vsize of a transaction equals to 3 times of the size with original serialization, plus the size with new serialization, divide the result by 4 and round up to the next integer. For example, if a transaction is 200 bytes with new serialization, and becomes 99 bytes with marker, flag, and witness removed, the vsize is (99 * 3 + 200) / 4 = 125 with round up.

I'm using this

Your README says to let you know if I'm using it. I'm letting you know.
Thanks!

Issue with coinselect/split

Hi, I think its just misusing but, lets say I have this

Utxos =>  [ { txId: '93bb26967bd8f62103dc07f7e08bd582d76634d531f3fd979ac30e1e8ac21f21',
    vout: 1,
    value: 14100000000 },
  { txId: '7a7761331adadc0df15add9dd4a63839b639420a632d1f809131dd37cf9bdc38',
    vout: 1,
    value: 14200000000 },
  { txId: 'f54fe83a01539c55e7ea228a3e555028873495b0cdee58e6e5f03f1e62897761',
    vout: 1,
    value: 143400000000 },
  { txId: 'd14f4e03b992d4bbe3ab894083fd7a151dad8c07e2d19e207d907840e7917665',
    vout: 1,
    value: 142200000000 } ]
Targets =>  [ { address: 'bU9uokogW4a8nCqXPTEmwtDoYcC3oJZmLH' } ]

I want a spend all to the address mentioned in targets, so I set no value.

I get an interesting output.. :

Inputs =>  [ { txId: '93bb26967bd8f62103dc07f7e08bd582d76634d531f3fd979ac30e1e8ac21f21',
    vout: 1,
    value: 14100000000 },
  { txId: '7a7761331adadc0df15add9dd4a63839b639420a632d1f809131dd37cf9bdc38',
    vout: 1,
    value: 14200000000 },
  { txId: 'f54fe83a01539c55e7ea228a3e555028873495b0cdee58e6e5f03f1e62897761',
    vout: 1,
    value: 143400000000 },
  { txId: 'd14f4e03b992d4bbe3ab894083fd7a151dad8c07e2d19e207d907840e7917665',
    vout: 1,
    value: 142200000000 } ]
outputs =>  [ { address: 'bU9uokogW4a8nCqXPTEmwtDoYcC3oJZmLH',
    value: 367387392 },
  { value: 313532612608 } ]

As you can see, the target will only receive 367387392, and the change (I set it after) will receive the rest.......
My code is the same as the example, except I use split algorithm.

Segwit workaround safe to use?

I've observed this library does not fully support Segwit.
However, it's still used in important projects such as BlueWallet.

I decided to open an issue so that we can discuss about possible workarounds and typical questions that may arise.

In the BlueWallet project they do some "tricks":

https://github.com/BlueWallet/BlueWallet/blob/master/class/wallets/abstract-hd-electrum-wallet.js

They inject a script structure in each utxo to account for Segwit.
From the comments in the code, this works when using Native Segwit and Nested Segwit addresses only:

// this is a hacky way to distinguish native/wrapped segwit, but its good enough for our case since we have only
// those 2 wallet types
if (this._getExternalAddressByIndex(0).startsWith('bc1')) {
  u.script = { length: 27 };
} else if (this._getExternalAddressByIndex(0).startsWith('3')) {
   u.script = { length: 50 };
}

Is it there anything that we should wary about doing that?
Can this still be used in transactions that have a mix of p2pkh and segwit (native and nested) inputs? (not adding a script structure in P2PKH utxos and adding length:27 or 50 for native and nested utxos, respectively?)

Also, in the target utxo they add 3 bytes for some reason (only if bech32, not if p2sh-p2wpkh).

if (t.address.startsWith('bc1')) {
  // in case address is non-typical and takes more bytes than coinselect library anticipates by default
  t.script = { length: bitcoin.address.toOutputScript(t.address).length + 3 };
}

Does anyone know what do they mean by non-typical address?

In general, will this trick break things if using taproot?

Thanks!

Can the coinSelect recognize that my utxos are from segwit?

Can the coinSelect recognize that my utxos are from segwit? (I think segwit will be lower fee cost?)

Looking forward to your reply!

Here is my test code:

    let coinSelect = require('coinselect');

    let feeRate = 21 // satoshis per byte 
    let utxos = [
        {
            "txid": "a14838af2970d92b3175cbde3bbf158fef8d740fca0d1ba7f74687e2b19c0eae",
            "vout": 0,
            "value": 30000,
            "amount": 0.0003,
            // "address": "3D8UZerWJtKqdWLZc4sFLsXoe9RTrt6nob",
            // "scriptPubKey": "a9147d7a053d8a8ba53b12ef29bef804b83c34a8963487",
        },
        {
            "txid": "abe6dd9b5cca3af9ba1a476b7ef09f0cfde08b04a869e77bf029d564b5b7fd23",
            "vout": 0,
            "value": 2000000,
            "amount": 0.02,
            // "address": "3FAwbExZBpcQ66MQZeEe9naH9j41jrLVHG",
            // "scriptPubKey": "a91493e1aa4fe49ff189f42ac0c54988c34237d090a387",
        },
    ];
    let targets = [
        {
            address: '33w5WjaVmba3qiTcz8ENi1mySHnmrS43M4',
            value: 2000001,
        }
    ];

    let {inputs, outputs, fee} = coinSelect(utxos, targets, feeRate)

// the accumulated fee is always returned for analysis
    console.log('fee', fee)
    console.log('inputs', inputs)
    console.log('outputs', outputs)

Possible conditional error on line 22 in blackjack.js

I was running into an issue passing the conditional on line 22 of blackjack.js

if ((inAccum + inputValue) > (outAccum + fee + threshold)) continue

despite having a greater sum of inAccum + inputValue than outAccum + fee + threshold

I changed the conditional to

if ((inAccum + inputValue) > (outAccum + fee + threshold)){

and was able to generate the correct response of hitting

return utils.finalize(inputs, outputs, feeRate)

The original error would read cannot forEach on inputs value of Undefined because I was only being returned a fee from the else

return { fee: feeRate * bytesAccum }

Implement BnB strategy

@xekyo wrote a master thesis, where he evaluated several coin selection strategies on a large dataset

http://murch.one/wp-content/uploads/2016/11/erhardt2016coinselection.pdf

The best strategy seems to be his own Branch and Bound strategy, that tries to look for exact match. It's defined in the thesis on pages 35 and forward.

The code seems to be here -https://github.com/Xekyo/CoinSelectionSimulator/blob/master/src/main/scala/one/murch/bitcoin/coinselection/StackEfficientTailRecursiveBnB.scala - MIT license

I am now trying to port the algorithm to javascript and to this module style.

See also bitcoin/bitcoin#7664 (comment) , murchandamus/CoinSelectionSimulator#3

edit: And also see c++ implementation here bitcoin/bitcoin#10637

Consolidate all strategy..?

I'm looking for a strategy to consolidate all inputs in a single output.
Yes this completely destroys privacy but it allows to consolidate during the period of low fees to prepare for a period of high fees.
Looks like coinselect/split is what might work, but asking just for any case.
Or can implement and send a PR if you approve this idea.

Module cannot be found , npm I —dev-save @types/coinselect

My coinselect module was working fine but two days ago it starts telling me module cannot be found , I have tried everything including adding js extensions to the module import , nothing seems to be working, I am using an esm module type in my package.json file , every other module is importing fine including bitcoinjs-lib but only coinselect and please is there any better alternative

TransactionBuilder is now deprecated

the main page shows an example using the TransactionBuilder. apparently we now need to use the Psbt class instead. could we modify the read me to use this new method?

BIP 125 selection for replacement

Replace by fee BIP125 states that at least one input in the transaction needs to be reused in the replacing transaction. Is this something this library should cater for? Such that you also provide the inputs from the previous transaction and the coin selection will ensure that at least one of them is used.

Alternatively are there any workarounds for using this library for RBF transactions?

How do you create a 'wallet' that has getChangeAddress()?

On the readme there is:

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)
})

where does wallet come from?

Layer for confirmations

I am thinking about adding another "layer" for coin selection, but I am not sure if to take it here or to a separate library.

Some inputs are confirmed and some are unconfirmed; some are from user himself and some are from other people. In coin selection, you first want to try only the confirmed inputs with 6+ confs, except for your own inputs that you can spend with 1+ conf, and only then use unconfirmed; except for coinbase inputs, that need to be confirmed with 50+ confs (I think).

However, right now, inputs in coinselect don't have information about that, and doesn't take that into account.

Do you think it should be here, or in another library?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.