@ -827,7 +827,7 @@ WalletService.prototype._getUtxosForAddresses = function(addresses, cb) {
var u = _ . pick ( utxo , [ 'txid' , 'vout' , 'address' , 'scriptPubKey' , 'amount' , 'satoshis' , 'confirmations' ] ) ;
u . confirmations = u . confirmations || 0 ;
u . locked = false ;
u . satoshis = u . satoshis ? + u . satoshis : Utils . strip ( u . amount * 1e8 ) ;
u . satoshis = _ . isNumber ( u . satoshis ) ? + u . satoshis : Utils . strip ( u . amount * 1e8 ) ;
delete u . amount ;
return u ;
} ) ;
@ -1026,8 +1026,43 @@ WalletService.prototype.getFeeLevels = function(opts, cb) {
} ) ;
} ;
WalletService . prototype . _ checkTxAndEstimateFee = function ( txp ) {
var bitcoreError ;
var serializationOpts = {
disableIsFullySigned : true
} ;
if ( ! _ . startsWith ( txp . version , '1.' ) ) {
serializationOpts . disableSmallFees = true ;
serializationOpts . disableLargeFees = true ;
}
try {
txp . estimateFee ( ) ;
var bitcoreTx = txp . getBitcoreTx ( ) ;
bitcoreError = bitcoreTx . getSerializationError ( serializationOpts ) ;
if ( ! bitcoreError ) {
txp . fee = bitcoreTx . getFee ( ) ;
}
} catch ( ex ) {
log . error ( 'Error building Bitcore transaction' , ex ) ;
return ex ;
}
if ( bitcoreError instanceof Bitcore . errors . Transaction . FeeError )
return Errors . INSUFFICIENT_FUNDS_FOR_FEE ;
if ( bitcoreError instanceof Bitcore . errors . Transaction . DustOutputs )
return Errors . DUST_AMOUNT ;
return bitcoreError ;
} ;
WalletService . prototype . _ selectTxInputs = function ( txp , utxosToExclude , cb ) {
var self = this ;
//todo: check inputs are ours and has enough value
if ( txp . inputs && txp . inputs . length > 0 ) {
return cb ( self . _ checkTxAndEstimateFee ( txp ) ) ;
}
function sortUtxos ( utxos ) {
var list = _ . map ( utxos , function ( utxo ) {
@ -1086,13 +1121,6 @@ WalletService.prototype._selectTxInputs = function(txp, utxosToExclude, cb) {
var inputs = sortUtxos ( utxos ) ;
var bitcoreTx , bitcoreError ;
var serializationOpts = {
disableIsFullySigned : true ,
} ;
if ( ! _ . startsWith ( txp . version , '1.' ) ) {
serializationOpts . disableSmallFees = true ;
serializationOpts . disableLargeFees = true ;
}
while ( i < inputs . length ) {
selected . push ( inputs [ i ] ) ;
@ -1100,28 +1128,14 @@ WalletService.prototype._selectTxInputs = function(txp, utxosToExclude, cb) {
i ++ ;
if ( total >= txp . getTotalAmount ( ) ) {
try {
txp . setInputs ( selected ) ;
txp . estimateFee ( ) ;
bitcoreTx = txp . getBitcoreTx ( ) ;
bitcoreError = bitcoreTx . getSerializationError ( serializationOpts ) ;
bitcoreError = self . _ checkTxAndEstimateFee ( txp ) ;
if ( ! bitcoreError ) {
txp . fee = bitcoreTx . getFee ( ) ;
return cb ( ) ;
}
} catch ( ex ) {
log . error ( 'Error building Bitcore transaction' , ex ) ;
return cb ( ex ) ;
}
}
}
if ( bitcoreError instanceof Bitcore . errors . Transaction . FeeError )
return cb ( Errors . INSUFFICIENT_FUNDS_FOR_FEE ) ;
if ( bitcoreError instanceof Bitcore . errors . Transaction . DustOutputs )
return cb ( Errors . DUST_AMOUNT ) ;
return cb ( bitcoreError || new Error ( 'Could not select tx inputs' ) ) ;
} ) ;
} ;
@ -1155,6 +1169,34 @@ WalletService.prototype._canCreateTx = function(copayerId, cb) {
} ) ;
} ;
WalletService . prototype . _ validateOutputs = function ( opts , wallet ) {
for ( var i = 0 ; i < opts . outputs . length ; i ++ ) {
var output = opts . outputs [ i ] ;
output . valid = false ;
if ( ! Utils . checkRequired ( output , [ 'toAddress' , 'amount' ] ) ) {
return new ClientError ( 'Required outputs argument missing' ) ;
}
var toAddress = { } ;
try {
toAddress = new Bitcore . Address ( output . toAddress ) ;
} catch ( ex ) {
return Errors . INVALID_ADDRESS ;
}
if ( toAddress . network != wallet . getNetworkName ( ) ) {
return Errors . INCORRECT_ADDRESS_NETWORK ;
}
if ( ! _ . isNumber ( output . amount ) || _ . isNaN ( output . amount ) || output . amount <= 0 ) {
return new ClientError ( 'Invalid amount' ) ;
}
if ( output . amount < Bitcore . Transaction . DUST_AMOUNT ) {
return Errors . DUST_AMOUNT ;
}
output . valid = true ;
}
return null ;
} ;
WalletService . _ getProposalHash = function ( proposalHeader ) {
function getOldHash ( toAddress , amount , message , payProUrl ) {
@ -1178,6 +1220,7 @@ WalletService._getProposalHash = function(proposalHeader) {
* @ param { string } opts . outputs [ ] . message - A message to attach to this output .
* @ param { string } opts . message - A message to attach to this transaction .
* @ param { string } opts . proposalSignature - S ( toAddress | amount | message | payProUrl ) . Used by other copayers to verify the proposal .
* @ param { string } opts . inputs - Optional . Inputs for this TX
* @ param { string } opts . feePerKb - Optional : Use an alternative fee per KB for this TX
* @ param { string } opts . payProUrl - Optional : Paypro URL for peers to verify TX
* @ param { string } opts . excludeUnconfirmedUtxos - Optional : Do not use UTXOs of unconfirmed transactions as inputs
@ -1198,17 +1241,6 @@ WalletService.prototype.createTx = function(opts, cb) {
if ( ! Model . TxProposal . isTypeSupported ( type ) )
return cb ( new ClientError ( 'Invalid proposal type' ) ) ;
_ . each ( opts . outputs , function ( output ) {
if ( ! Utils . checkRequired ( output , [ 'toAddress' , 'amount' ] ) ) {
output . valid = false ;
cb ( new ClientError ( 'Required outputs argument missing' ) ) ;
return false ;
}
} ) ;
if ( _ . any ( opts . outputs , {
valid : false
} ) ) return ;
var feePerKb = opts . feePerKb || Defaults . DEFAULT_FEE_PER_KB ;
if ( feePerKb < Defaults . MIN_FEE_PER_KB || feePerKb > Defaults . MAX_FEE_PER_KB )
return cb ( new ClientError ( 'Invalid fee per KB value' ) ) ;
@ -1242,38 +1274,19 @@ WalletService.prototype.createTx = function(opts, cb) {
if ( err ) return cb ( err ) ;
if ( ! canCreate ) return cb ( Errors . TX_CANNOT_CREATE ) ;
_ . each ( opts . outputs , function ( output ) {
output . valid = false ;
var toAddress = { } ;
try {
toAddress = new Bitcore . Address ( output . toAddress ) ;
} catch ( ex ) {
cb ( Errors . INVALID_ADDRESS ) ;
return false ;
if ( type != Model . TxProposal . Types . EXTERNAL ) {
var validationError = self . _ validateOutputs ( opts , wallet ) ;
if ( validationError ) {
return cb ( validationError ) ;
}
if ( toAddress . network != wallet . getNetworkName ( ) ) {
cb ( Errors . INCORRECT_ADDRESS_NETWORK ) ;
return false ;
}
if ( ! _ . isNumber ( output . amount ) || _ . isNaN ( output . amount ) || output . amount <= 0 ) {
cb ( new ClientError ( 'Invalid amount' ) ) ;
return false ;
}
if ( output . amount < Bitcore . Transaction . DUST_AMOUNT ) {
cb ( Errors . DUST_AMOUNT ) ;
return false ;
}
output . valid = true ;
} ) ;
if ( _ . any ( opts . outputs , {
valid : false
} ) ) return ;
var txOpts = {
type : type ,
walletId : self . walletId ,
creatorId : self . copayerId ,
outputs : opts . outputs ,
inputs : opts . inputs ,
toAddress : opts . toAddress ,
amount : opts . amount ,
message : opts . message ,