@ -1230,7 +1230,11 @@ WalletService.prototype.getFeeLevels = function(opts, cb) {
} ) ;
} ;
WalletService . prototype . _ checkTxAndEstimateFee = function ( txp ) {
WalletService . prototype . _ estimateFee = function ( txp ) {
txp . estimateFee ( ) ;
} ;
WalletService . prototype . _ checkTx = function ( txp ) {
var bitcoreError ;
var serializationOpts = {
@ -1241,8 +1245,6 @@ WalletService.prototype._checkTxAndEstimateFee = function(txp) {
serializationOpts . disableLargeFees = true ;
}
txp . estimateFee ( ) ;
if ( txp . getEstimatedSize ( ) / 1000 > Defaults . MAX_TX_SIZE_IN_KB )
return Errors . TX_MAX_SIZE_EXCEEDED ;
@ -1270,7 +1272,8 @@ WalletService.prototype._selectTxInputs = function(txp, utxosToExclude, cb) {
//todo: check inputs are ours and has enough value
if ( txp . inputs && txp . inputs . length > 0 ) {
return cb ( self . _ checkTxAndEstimateFee ( txp ) ) ;
self . _ estimateFee ( txp ) ;
return cb ( self . _ checkTx ( txp ) ) ;
}
var txpAmount = txp . getTotalAmount ( ) ;
@ -1331,7 +1334,9 @@ WalletService.prototype._selectTxInputs = function(txp, utxosToExclude, cb) {
log . debug ( 'Considering ' + smallInputs . length + ' small inputs (' + Utils . formatUtxos ( smallInputs ) + ')' ) ;
var total = 0 ;
var netTotal = 0 ;
var selected = [ ] ;
var fee ;
var error ;
_ . each ( smallInputs , function ( input , i ) {
@ -1342,20 +1347,23 @@ WalletService.prototype._selectTxInputs = function(txp, utxosToExclude, cb) {
return false ;
}
var inputAmount = input . satoshis - feePerInput ;
log . debug ( 'The input contributes ' + Utils . formatAmountInBtc ( inputAmount ) ) ;
var netInputAmount = input . satoshis - feePerInput ;
log . debug ( 'The input contributes ' + Utils . formatAmountInBtc ( netInputAmount ) ) ;
selected . push ( input ) ;
total += inputAmount ;
total += input . satoshis ;
netTotal += netInputAmount ;
var txpSize = baseTxpSize + selected . length * sizePerInput ;
var txpFee = baseTxpFee + selected . length * feePerInput ;
fee = Math . round ( baseTxpFee + selected . length * feePerInput ) ;
log . debug ( 'Tx size: ' + Utils . formatSize ( txpSize ) + ', Tx fee: ' + Utils . formatAmountInBtc ( txpF ee) ) ;
log . debug ( 'Tx size: ' + Utils . formatSize ( txpSize ) + ', Tx fee: ' + Utils . formatAmountInBtc ( f ee) ) ;
var feeVsAmountRatio = txpF ee / txpAmount ;
var feeVsSingleInputFeeRatio = txpF ee / ( baseTxpFee + feePerInput ) ;
var amountVsUtxoRatio = i nputAmount / txpAmount ;
var feeVsAmountRatio = f ee / txpAmount ;
var feeVsSingleInputFeeRatio = f ee / ( baseTxpFee + feePerInput ) ;
var amountVsUtxoRatio = netI nputAmount / txpAmount ;
log . debug ( 'Tx amount/Fee: ' + Utils . formatRatio ( feeVsAmountRatio ) + ' (max: ' + Utils . formatRatio ( Defaults . UTXO_SELECTION_MAX_FEE_VS_TX_AMOUNT_FACTOR ) + ')' ) ;
log . debug ( 'Single-input fee/Multi-input fee: ' + Utils . formatRatio ( feeVsSingleInputFeeRatio ) + ' (max: ' + Utils . formatRatio ( Defaults . UTXO_SELECTION_MAX_FEE_VS_SINGLE_UTXO_FEE_FACTOR ) + ')' + ' loses wrt single-input tx: ' + Utils . formatAmountInBtc ( ( selected . length - 1 ) * feePerInput ) ) ;
@ -1380,29 +1388,34 @@ WalletService.prototype._selectTxInputs = function(txp, utxosToExclude, cb) {
}
}
log . debug ( 'Cumuled total so far: ' + Utils . formatAmountInBtc ( total ) ) ;
log . debug ( 'Cumuled total so far: ' + Utils . formatAmountInBtc ( total ) + ', Net total so far: ' + Utils . formatAmountInBtc ( netTotal ) ) ;
if ( netTotal >= txpAmount ) {
var changeAmount = Math . round ( total - txpAmount - fee ) ;
if ( total >= txpAmount ) {
var changeAmount = total - txpAmount - txpFee ;
log . debug ( 'Tx change: ' , Utils . formatAmountInBtc ( changeAmount ) ) ;
if ( changeAmount <= Bitcore . Transaction . DUST_AMOUNT ) {
log . debug ( 'Change (' + Utils . formatAmountInBtc ( changeAmount ) + ') below dust amount (' + Utils . formatAmountInBtc ( Bitcore . Transaction . DUST_AMOUNT ) + ')' ) ;
return ;
if ( changeAmount != 0 && Math . abs ( changeAmount ) <= Bitcore . Transaction . DUST_AMOUNT ) {
log . debug ( 'ABS(Change) (' + Utils . formatAmountInBtc ( changeAmount ) + ') below dust amount (' + Utils . formatAmountInBtc ( Bitcore . Transaction . DUST_AMOUNT ) + ')' ) ;
if ( changeAmount > 0 && fee < changeAmount ) return ;
// Either increment or decrement fee to remove change
fee += changeAmount ;
}
return false ;
}
} ) ;
if ( total < txpAmount ) {
log . debug ( 'Could not reach Txp total (' + Utils . formatAmountInBtc ( txpAmount ) + '), still missing: ' + Utils . formatAmountInBtc ( txpAmount - total ) ) ;
if ( ne tT otal < txpAmount ) {
log . debug ( 'Could not reach Txp total (' + Utils . formatAmountInBtc ( txpAmount ) + '), still missing: ' + Utils . formatAmountInBtc ( txpAmount - ne tT otal) ) ;
selected = [ ] ;
if ( ! _ . isEmpty ( bigInputs ) ) {
log . debug ( 'Using big input: ' , Utils . formatUtxos ( _ . first ( bigInputs ) ) ) ;
var input = _ . first ( bigInputs ) ;
log . debug ( 'Using big input: ' , Utils . formatUtxos ( input ) ) ;
total = input . satoshis ;
fee = Math . round ( baseTxpFee + feePerInput ) ;
netTotal = total - fee ;
selected = [ input ] ;
}
}
@ -1412,7 +1425,7 @@ WalletService.prototype._selectTxInputs = function(txp, utxosToExclude, cb) {
return cb ( error || Errors . INSUFFICIENT_FUNDS_FOR_FEE ) ;
}
return cb ( null , selected ) ;
return cb ( null , selected , fee ) ;
} ;
log . debug ( 'Selecting inputs for a ' + Utils . formatAmountInBtc ( txp . getTotalAmount ( ) ) + ' txp' ) ;
@ -1443,6 +1456,7 @@ WalletService.prototype._selectTxInputs = function(txp, utxosToExclude, cb) {
if ( ! txp . excludeUnconfirmedUtxos ) groups . push ( 0 ) ;
var inputs = [ ] ;
var fee ;
var selectionError ;
var i = 0 ;
var lastGroupLength ;
@ -1467,7 +1481,7 @@ WalletService.prototype._selectTxInputs = function(txp, utxosToExclude, cb) {
lastGroupLength = candidateUtxos . length ;
select ( candidateUtxos , function ( err , selected ) {
select ( candidateUtxos , function ( err , selectedInputs , selectedFee ) {
if ( err ) {
log . debug ( 'No inputs selected on this group: ' , err ) ;
selectionError = err ;
@ -1475,9 +1489,12 @@ WalletService.prototype._selectTxInputs = function(txp, utxosToExclude, cb) {
}
selectionError = null ;
inputs = selected ;
inputs = selectedInputs ;
fee = selectedFee ;
log . debug ( 'Selected inputs from this group: ' + Utils . formatUtxos ( inputs ) ) ;
log . debug ( 'Fee for this selection: ' + Utils . formatAmountInBtc ( fee ) ) ;
return next ( ) ;
} ) ;
} , function ( err ) {
@ -1485,8 +1502,9 @@ WalletService.prototype._selectTxInputs = function(txp, utxosToExclude, cb) {
if ( selectionError || _ . isEmpty ( inputs ) ) return cb ( selectionError || new Error ( 'Could not select tx inputs' ) ) ;
txp . setInputs ( inputs ) ;
txp . fee = fee ;
var err = self . _ checkTxAndEstimateFee ( txp ) ;
var err = self . _ checkTx ( txp ) ;
if ( ! err ) {
log . debug ( 'Successfully built transaction. Total fees: ' + Utils . formatAmountInBtc ( txp . fee ) + ', total change: ' + Utils . formatAmountInBtc ( _ . sum ( txp . inputs , 'satoshis' ) - txp . fee ) ) ;