better utxo selection, combining inputs different confimations steps
This commit is contained in:
parent
aac13a8817
commit
42d30f44a8
|
@ -683,51 +683,12 @@ Transaction.prototype.parse = function (parser) {
|
||||||
this.calcHash();
|
this.calcHash();
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
* _selectUnspent
|
|
||||||
*
|
|
||||||
* Selects some unspend outputs for later usage in tx inputs
|
|
||||||
*
|
|
||||||
* @unspentArray: unspent array (UTXO) avaible on the form (see selectUnspent)
|
|
||||||
* @totalNeededAmount: output transaction amount in BTC, including fee
|
|
||||||
* @minConfirmations: 0 by default.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* Returns the selected outputs or null if there are not enough funds.
|
|
||||||
* The utxos are selected in the order they appear in the original array.
|
|
||||||
* Sorting must be done previusly.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
Transaction._selectUnspent = function (unspentArray, totalNeededAmount, minConfirmations) {
|
|
||||||
minConfirmations = minConfirmations || 0;
|
|
||||||
|
|
||||||
var selected = [];
|
|
||||||
var l = unspentArray.length;
|
|
||||||
var totalSat = bignum(0);
|
|
||||||
var totalNeededAmountSat = util.parseValue(totalNeededAmount);
|
|
||||||
var fullfill = false;
|
|
||||||
|
|
||||||
for(var i = 0; i<l; i++) {
|
|
||||||
var u = unspentArray[i];
|
|
||||||
if ( (u.confirmations||0) < minConfirmations)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var sat = u.amountSat || util.parseValue(u.amount);
|
|
||||||
totalSat = totalSat.add(sat);
|
|
||||||
selected.push(u);
|
|
||||||
if(totalSat.cmp(totalNeededAmountSat) >= 0) {
|
|
||||||
fullfill = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!fullfill) return [];
|
|
||||||
return selected;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* selectUnspent
|
* selectUnspent
|
||||||
*
|
*
|
||||||
* Selects some unspend outputs for later usage in tx inputs
|
* Selects some unspent outputs for later usage in tx inputs
|
||||||
*
|
*
|
||||||
* @unspentArray: unspent array (UTXO) avaible on the form:
|
* @unspentArray: unspent array (UTXO) avaible on the form:
|
||||||
* [{
|
* [{
|
||||||
|
@ -739,28 +700,56 @@ Transaction._selectUnspent = function (unspentArray, totalNeededAmount, minConfi
|
||||||
* confirmations: 3
|
* confirmations: 3
|
||||||
* }, [...]
|
* }, [...]
|
||||||
* ]
|
* ]
|
||||||
* This is compatible con insight's /utxo API.
|
* This is compatible con insight's utxo API.
|
||||||
* That amount is in BTCs. (as returned in insight and bitcoind)
|
* That amount is in BTCs (as returned in insight and bitcoind).
|
||||||
* amountSat can be given to provide amount in satochis.
|
* amountSat (instead of amount) can be given to provide amount in satochis.
|
||||||
*
|
*
|
||||||
* @totalNeededAmount: output transaction amount in BTC, including fee
|
* @totalNeededAmount: output transaction amount in BTC, including fee
|
||||||
* @allowUnconfirmed:false (allow selecting unconfirmed utxos)
|
* @allowUnconfirmed: false (allow selecting unconfirmed utxos)
|
||||||
*
|
|
||||||
*
|
*
|
||||||
* Note that the sum of the selected unspent is >= the desired amount.
|
* Note that the sum of the selected unspent is >= the desired amount.
|
||||||
|
* Returns the selected unspent outputs if the totalNeededAmount was reach.
|
||||||
|
* 'null' if not.
|
||||||
|
*
|
||||||
|
* TODO: utxo selection is not optimized to minimize mempool usage.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Transaction.selectUnspent = function (unspentArray, totalNeededAmount, allowUnconfirmed) {
|
Transaction.selectUnspent = function (unspentArray, totalNeededAmount, allowUnconfirmed) {
|
||||||
var answer = Transaction._selectUnspent(unspentArray, totalNeededAmount, 6);
|
|
||||||
|
|
||||||
if (!answer.length)
|
var minConfirmationSteps = [6,1];
|
||||||
answer = Transaction._selectUnspent(unspentArray, totalNeededAmount, 1);
|
if (allowUnconfirmed) minConfirmationSteps.push(0);
|
||||||
|
|
||||||
if (!answer.length && allowUnconfirmed)
|
var ret = [];
|
||||||
answer = Transaction._selectUnspent(unspentArray, totalNeededAmount, 0);
|
var l = unspentArray.length;
|
||||||
|
var totalSat = bignum(0);
|
||||||
|
var totalNeededAmountSat = util.parseValue(totalNeededAmount);
|
||||||
|
var fulfill = false;
|
||||||
|
var maxConfirmations = null;
|
||||||
|
|
||||||
return answer;
|
do {
|
||||||
|
var minConfirmations = minConfirmationSteps.shift();
|
||||||
|
for(var i = 0; i<l; i++) {
|
||||||
|
var u = unspentArray[i];
|
||||||
|
|
||||||
|
var c = u.confirmations || 0;
|
||||||
|
|
||||||
|
if ( c < minConfirmations || (maxConfirmations && c >=maxConfirmations) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
|
||||||
|
var sat = u.amountSat || util.parseValue(u.amount);
|
||||||
|
totalSat = totalSat.add(sat);
|
||||||
|
ret.push(u);
|
||||||
|
if(totalSat.cmp(totalNeededAmountSat) >= 0) {
|
||||||
|
fulfill = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
maxConfirmations = minConfirmations;
|
||||||
|
} while( !fulfill && minConfirmationSteps.length);
|
||||||
|
|
||||||
|
return fulfill ? ret : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -34,41 +34,47 @@ describe('Transaction', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('#_selectUnspent should be able to select utxos', function() {
|
it('#selectUnspent should be able to select utxos', function() {
|
||||||
var u = Transaction._selectUnspent(testdata.dataUnspent,1.0);
|
var u = Transaction.selectUnspent(testdata.dataUnspent,1.0, true);
|
||||||
u.length.should.equal(3);
|
u.length.should.equal(3);
|
||||||
u = Transaction._selectUnspent(testdata.dataUnspent,0.5);
|
|
||||||
u.length.should.equal(3);
|
|
||||||
u = Transaction._selectUnspent(testdata.dataUnspent,0.1);
|
|
||||||
u.length.should.equal(2);
|
|
||||||
u = Transaction._selectUnspent(testdata.dataUnspent,0.05);
|
|
||||||
u.length.should.equal(2);
|
|
||||||
u = Transaction._selectUnspent(testdata.dataUnspent,0.015);
|
|
||||||
u.length.should.equal(2);
|
|
||||||
u = Transaction._selectUnspent(testdata.dataUnspent,0.01);
|
|
||||||
u.length.should.equal(1);
|
|
||||||
should.exist(u[0].amount);
|
should.exist(u[0].amount);
|
||||||
should.exist(u[0].txid);
|
should.exist(u[0].txid);
|
||||||
should.exist(u[0].scriptPubKey);
|
should.exist(u[0].scriptPubKey);
|
||||||
should.exist(u[0].vout);
|
should.exist(u[0].vout);
|
||||||
|
|
||||||
|
u = Transaction.selectUnspent(testdata.dataUnspent,0.5, true);
|
||||||
|
u.length.should.equal(3);
|
||||||
|
|
||||||
|
u = Transaction.selectUnspent(testdata.dataUnspent,0.1, true);
|
||||||
|
u.length.should.equal(2);
|
||||||
|
|
||||||
|
u = Transaction.selectUnspent(testdata.dataUnspent,0.05, true);
|
||||||
|
u.length.should.equal(2);
|
||||||
|
|
||||||
|
u = Transaction.selectUnspent(testdata.dataUnspent,0.015, true);
|
||||||
|
u.length.should.equal(2);
|
||||||
|
|
||||||
|
u = Transaction.selectUnspent(testdata.dataUnspent,0.01, true);
|
||||||
|
u.length.should.equal(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#selectUnspent should return null if not enough utxos', function() {
|
it('#selectUnspent should return null if not enough utxos', function() {
|
||||||
var u = Transaction.selectUnspent(testdata.dataUnspent,1.12);
|
var u = Transaction.selectUnspent(testdata.dataUnspent,1.12);
|
||||||
u.length.should.equal(0);
|
should.not.exist(u);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('#selectUnspent should check confirmations', function() {
|
it('#selectUnspent should check confirmations', function() {
|
||||||
var u = Transaction.selectUnspent(testdata.dataUnspent,0.9);
|
var u = Transaction.selectUnspent(testdata.dataUnspent,0.9);
|
||||||
u.length.should.equal(0);
|
should.not.exist(u);
|
||||||
var u = Transaction.selectUnspent(testdata.dataUnspent,0.9,true);
|
var u = Transaction.selectUnspent(testdata.dataUnspent,0.9,true);
|
||||||
u.length.should.equal(3);
|
u.length.should.equal(3);
|
||||||
|
|
||||||
var u = Transaction.selectUnspent(testdata.dataUnspent,0.11);
|
var u = Transaction.selectUnspent(testdata.dataUnspent,0.11);
|
||||||
u.length.should.equal(2);
|
u.length.should.equal(2);
|
||||||
var u = Transaction.selectUnspent(testdata.dataUnspent,0.111);
|
var u = Transaction.selectUnspent(testdata.dataUnspent,0.111);
|
||||||
u.length.should.equal(0);
|
should.not.exist(u);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue