Merge pull request #3134 from BlueWallet/fix-better-utxo-selection

REF: smarter utxo selection (closes #3085)
This commit is contained in:
GLaDOS 2021-05-11 12:35:18 +01:00 committed by GitHub
commit 7e99b5b7fe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 47 additions and 6 deletions

View file

@ -5,7 +5,7 @@ import { AbstractWallet } from './abstract-wallet';
import { HDSegwitBech32Wallet } from '..';
const bitcoin = require('bitcoinjs-lib');
const BlueElectrum = require('../../blue_modules/BlueElectrum');
const coinSelectAccumulative = require('coinselect/accumulative');
const coinSelect = require('coinselect');
const coinSelectSplit = require('coinselect/split');
/**
@ -337,7 +337,7 @@ export class LegacyWallet extends AbstractWallet {
coinselect(utxos, targets, feeRate, changeAddress) {
if (!changeAddress) throw new Error('No change address provided');
let algo = coinSelectAccumulative;
let algo = coinSelect;
// if targets has output without a value, we want send MAX to it
if (targets.some(i => !('value' in i))) {
algo = coinSelectSplit;

View file

@ -528,7 +528,7 @@ describe('BlueWallet UI Tests', () => {
await yo('TransactionValue');
expect(element(by.id('TransactionValue'))).toHaveText('0.0001');
const transactionFee = await extractTextFromElementById('TransactionFee');
assert.ok(transactionFee.startsWith('Fee: 0.00000748 BTC'), 'Unexpected tx fee: ' + transactionFee);
assert.ok(transactionFee.startsWith('Fee: 0.00000452 BTC'), 'Unexpected tx fee: ' + transactionFee);
await element(by.id('TransactionDetailsButton')).tap();
let txhex = await extractTextFromElementById('TxhexInput');
@ -540,7 +540,7 @@ describe('BlueWallet UI Tests', () => {
assert.strictEqual(transaction.outs[0].value, 10000);
// checking fee rate:
const totalIns = 100000 + 5526; // we hardcode it since we know it in advance
const totalIns = 100000; // we hardcode it since we know it in advance
const totalOuts = transaction.outs.map(el => el.value).reduce((a, b) => a + b, 0);
assert.strictEqual(Math.round((totalIns - totalOuts) / (txhex.length / 2)), feeRate);
assert.strictEqual(transactionFee.split(' ')[1] * 100000000, totalIns - totalOuts);

View file

@ -110,10 +110,10 @@ describe('Legacy HD (BIP44)', () => {
hd._getInternalAddressByIndex(hd.next_free_change_address_index),
);
let tx = bitcoin.Transaction.fromHex(txNew.tx.toHex());
assert.strictEqual(tx.ins.length, 4);
assert.strictEqual(tx.ins.length, 3);
assert.strictEqual(tx.outs.length, 2);
assert.strictEqual(tx.outs[0].value, 80000); // payee
assert.strictEqual(tx.outs[1].value, 19330); // change
assert.strictEqual(tx.outs[1].value, 9478); // change
let toAddress = bitcoin.address.fromOutputScript(tx.outs[0].script);
const changeAddress = bitcoin.address.fromOutputScript(tx.outs[1].script);
assert.strictEqual('3GcKN7q7gZuZ8eHygAhHrvPa5zZbG5Q1rK', toAddress);

View file

@ -53,6 +53,47 @@ describe('Legacy wallet', () => {
assert.strictEqual('bc1q3rl0mkyk0zrtxfmqn9wpcd3gnaz00yv9yp0hxe', bitcoin.address.fromOutputScript(tx.outs[1].script)); // to address
});
it('can create transaction with better UTXO selection', async () => {
const l = new LegacyWallet();
l.setSecret('L4ccWrPMmFDZw4kzAKFqJNxgHANjdy6b7YKNXMwB4xac4FLF3Tov');
const utxos = [
{
txid: 'cc44e933a094296d9fe424ad7306f16916253a3d154d52e4f1a757c18242cec4',
vout: 0,
value: 1000,
txhex:
'0200000000010161890cd52770c150da4d7d190920f43b9f88e7660c565a5a5ad141abb6de09de00000000000000008002a0860100000000001976a91426e01119d265aa980390c49eece923976c218f1588ac3e17000000000000160014c1af8c9dd85e0e55a532a952282604f820746fcd02473044022072b3f28808943c6aa588dd7a4e8f29fad7357a2814e05d6c5d767eb6b307b4e6022067bc6a8df2dbee43c87b8ce9ddd9fe678e00e0f7ae6690d5cb81eca6170c47e8012102e8fba5643e15ab70ec79528833a2c51338c1114c4eebc348a235b1a3e13ab07100000000',
},
{
txid: 'cc44e933a094296d9fe424ad7306f16916253a3d154d52e4f1a757c18242cec4',
vout: 1,
value: 1000,
txhex:
'0200000000010161890cd52770c150da4d7d190920f43b9f88e7660c565a5a5ad141abb6de09de00000000000000008002a0860100000000001976a91426e01119d265aa980390c49eece923976c218f1588ac3e17000000000000160014c1af8c9dd85e0e55a532a952282604f820746fcd02473044022072b3f28808943c6aa588dd7a4e8f29fad7357a2814e05d6c5d767eb6b307b4e6022067bc6a8df2dbee43c87b8ce9ddd9fe678e00e0f7ae6690d5cb81eca6170c47e8012102e8fba5643e15ab70ec79528833a2c51338c1114c4eebc348a235b1a3e13ab07100000000',
},
{
txid: 'cc44e933a094296d9fe424ad7306f16916253a3d154d52e4f1a757c18242cec4',
vout: 2,
value: 69000000,
txhex:
'0200000000010161890cd52770c150da4d7d190920f43b9f88e7660c565a5a5ad141abb6de09de00000000000000008002a0860100000000001976a91426e01119d265aa980390c49eece923976c218f1588ac3e17000000000000160014c1af8c9dd85e0e55a532a952282604f820746fcd02473044022072b3f28808943c6aa588dd7a4e8f29fad7357a2814e05d6c5d767eb6b307b4e6022067bc6a8df2dbee43c87b8ce9ddd9fe678e00e0f7ae6690d5cb81eca6170c47e8012102e8fba5643e15ab70ec79528833a2c51338c1114c4eebc348a235b1a3e13ab07100000000',
},
];
// ^^ only non-segwit inputs need full transaction txhex
const { psbt } = l.createTransaction(
utxos,
[{ value: 60000000, address: '1GX36PGBUrF8XahZEGQqHqnJGW2vCZteoB' }],
1,
l.getAddress(),
0,
true,
);
assert.strictEqual(psbt.data.inputs.length, 1);
});
it("throws error if you can't create wallet from this entropy", async () => {
const l = new LegacyWallet();
const zeroes = [...Array(32)].map(() => 0);