bitcoinjs / coinselect Goto Github PK
View Code? Open in Web Editor NEWAn unspent transaction output (UTXO) selection module for bitcoin.
License: MIT License
An unspent transaction output (UTXO) selection module for bitcoin.
License: MIT License
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?
if the target value is 'matched', where do the fees come from?
Coinselection should prefer older utxos and use unconfirmed utxos the latest
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.
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!`
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!
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
this is very cool but how do I get a list of UTXOs given a private key?
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
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
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
}
satoshis per kB
than satoshis per byte
?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?
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 }
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)
@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
Your README says to let you know if I'm using it. I'm letting you know.
Thanks!
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?
What does this represent
return { fee: feeRate * bytesAccum }
at failed blackjack algorithm? It doesn't seem like any actual transaction :) and the fee doesn't correspond to much either
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?
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.
Just wanted to link that Trezor is using similar methodologies. Might be useful to someone else. Seems they try to support any UTXO-based currency. It's too bad they don't offer it as a separate package.
Is this project dead?
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 };
};
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.
Just looking for a Concept ACK before I start coding up the PR, I would hate to code the wrong thing. @junderw
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
}
]
Declaration file support will help a lot. I opened #77 for it. Any feedback is welcome.
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.
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:
Line 22 in 1f7cd37
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.
Line 34 in 1f7cd37
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?
I wrote this code as a demo, but I got undefined, what's wrong?
let { inputs, outputs, fee } = coinSelect(utxos, targets, feeRate);
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.
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!
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?
My native language is not English,I hope you can understand
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.
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:
Line 50 in 3c72703
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:
coinselect/test/fixtures/index.json
Line 618 in 3c72703
{
"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:
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.
Should dustThreshold in utils.js be different for segwit changes, or not?
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 )
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
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.
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?
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?
Float fee rates are not something uncommon. Something like 0.1
, 100.2
, ...
On the other hand, If We check uintOrNaN, float fee rates are not handled by the library
As much as I could see, it can be easily fixed by removing that line on utils uintOrNaN, setting Math.round()
on both accumulative and blackjack, and finalize
If there are many UTXOs being parsed into coinselect for a solution it does not appear to handle them and outputs no solution
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?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.