2014-03-09 19:31:58 -07:00
var redis = require ( 'redis' ) ;
2014-03-11 18:56:19 -07:00
var async = require ( 'async' ) ;
2014-03-09 19:31:58 -07:00
var Stratum = require ( 'stratum-pool' ) ;
2014-03-11 18:56:19 -07:00
2014-03-09 19:31:58 -07:00
module . exports = function ( logger ) {
var poolConfigs = JSON . parse ( process . env . pools ) ;
Object . keys ( poolConfigs ) . forEach ( function ( coin ) {
SetupForPool ( logger , poolConfigs [ coin ] ) ;
} ) ;
} ;
function SetupForPool ( logger , poolOptions ) {
2014-03-26 14:08:34 -07:00
if ( ! poolOptions . shareProcessing ||
2014-03-26 21:55:58 -07:00
! poolOptions . shareProcessing . internal ||
2014-03-26 14:08:34 -07:00
! poolOptions . shareProcessing . internal . enabled )
return ;
2014-03-09 19:31:58 -07:00
2014-03-26 14:08:34 -07:00
var coin = poolOptions . coin . name ;
2014-03-09 19:31:58 -07:00
var processingConfig = poolOptions . shareProcessing . internal ;
2014-03-26 14:08:34 -07:00
2014-03-09 19:31:58 -07:00
2014-03-22 23:16:06 -07:00
var logSystem = 'Payments' ;
var logComponent = coin ;
2014-03-09 19:31:58 -07:00
var daemon = new Stratum . daemon . interface ( [ processingConfig . daemon ] ) ;
daemon . once ( 'online' , function ( ) {
2014-03-22 23:16:06 -07:00
logger . debug ( logSystem , logComponent , 'Connected to daemon for payment processing' ) ;
2014-03-09 19:31:58 -07:00
daemon . cmd ( 'validateaddress' , [ poolOptions . address ] , function ( result ) {
2014-03-24 13:00:18 -07:00
if ( ! result [ 0 ] . response || ! result [ 0 ] . response . ismine ) {
2014-03-22 23:16:06 -07:00
logger . error ( logSystem , logComponent , 'Daemon does not own pool address - payment processing can not be done with this daemon' ) ;
2014-03-09 19:31:58 -07:00
}
} ) ;
} ) . once ( 'connectionFailed' , function ( error ) {
2014-03-22 23:16:06 -07:00
logger . error ( logSystem , logComponent , 'Failed to connect to daemon for payment processing: ' + JSON . stringify ( error ) ) ;
2014-03-06 12:46:01 -08:00
} ) . on ( 'error' , function ( error ) {
2014-03-22 23:16:06 -07:00
logger . error ( logSystem , logComponent ) ;
2014-03-09 19:31:58 -07:00
} ) . init ( ) ;
var redisClient ;
var connectToRedis = function ( ) {
var reconnectTimeout ;
redisClient = redis . createClient ( processingConfig . redis . port , processingConfig . redis . host ) ;
redisClient . on ( 'ready' , function ( ) {
clearTimeout ( reconnectTimeout ) ;
2014-03-22 23:16:06 -07:00
logger . debug ( logSystem , logComponent , 'Successfully connected to redis database' ) ;
2014-03-09 19:31:58 -07:00
} ) . on ( 'error' , function ( err ) {
paymentLogger . error ( 'redis' , 'Redis client had an error: ' + JSON . stringify ( err ) )
} ) . on ( 'end' , function ( ) {
2014-03-22 23:16:06 -07:00
logger . error ( logSystem , logComponent , 'Connection to redis database as been ended' ) ;
logger . warning ( logSystem , logComponent , 'Trying reconnection to redis in 3 seconds...' ) ;
2014-03-09 19:31:58 -07:00
reconnectTimeout = setTimeout ( function ( ) {
connectToRedis ( ) ;
} , 3000 ) ;
} ) ;
} ;
connectToRedis ( ) ;
2014-03-12 15:33:29 -07:00
2014-03-11 18:56:19 -07:00
var processPayments = function ( ) {
async . waterfall ( [
2014-03-12 15:33:29 -07:00
/ * C a l l r e d i s t o g e t a n a r r a y o f r o u n d s - w h i c h a r e c o i n b a s e t r a n s a c t i o n s a n d b l o c k h e i g h t s f r o m s u b m i t t e d
blocks . * /
2014-03-11 18:56:19 -07:00
function ( callback ) {
2014-03-11 21:57:03 -07:00
2014-03-20 15:25:59 -07:00
redisClient . smembers ( coin + '_blocksPending' , function ( error , results ) {
2014-03-11 20:47:14 -07:00
2014-03-11 18:56:19 -07:00
if ( error ) {
2014-03-22 23:16:06 -07:00
logger . error ( logSystem , logComponent , 'Could get blocks from redis ' + JSON . stringify ( error ) ) ;
2014-03-22 23:46:59 -07:00
callback ( 'Check finished - redis error for getting blocks' ) ;
2014-03-11 18:56:19 -07:00
return ;
}
if ( results . length === 0 ) {
2014-03-22 23:46:59 -07:00
callback ( 'Check finished - no pending blocks in redis' ) ;
2014-03-11 18:56:19 -07:00
return ;
}
2014-03-12 17:09:12 -07:00
var rounds = results . map ( function ( r ) {
var details = r . split ( ':' ) ;
2014-03-22 19:08:33 -07:00
return {
category : details [ 0 ] . category ,
txHash : details [ 0 ] ,
height : details [ 1 ] ,
reward : details [ 2 ] ,
serialized : r
} ;
2014-03-11 18:56:19 -07:00
} ) ;
2014-03-12 17:09:12 -07:00
2014-03-12 15:33:29 -07:00
callback ( null , rounds ) ;
2014-03-11 18:56:19 -07:00
} ) ;
} ,
2014-03-11 21:57:03 -07:00
2014-03-12 15:33:29 -07:00
/ * D o e s a b a t c h r p c c a l l t o d a e m o n w i t h a l l t h e t r a n s a c t i o n h a s h e s t o s e e i f t h e y a r e c o n f i r m e d y e t .
It also adds the block reward amount to the round object - which the daemon gives also gives us . * /
function ( rounds , callback ) {
2014-03-11 18:56:19 -07:00
2014-03-12 17:09:12 -07:00
var batchRPCcommand = rounds . map ( function ( r ) {
return [ 'gettransaction' , [ r . txHash ] ] ;
} ) ;
2014-03-11 18:56:19 -07:00
daemon . batchCmd ( batchRPCcommand , function ( error , txDetails ) {
2014-03-11 20:47:14 -07:00
if ( error || ! txDetails ) {
2014-03-22 23:46:59 -07:00
callback ( 'Check finished - daemon rpc error with batch gettransactions ' + JSON . stringify ( error ) ) ;
2014-03-11 20:47:14 -07:00
return ;
}
2014-03-18 23:54:18 -07:00
txDetails = txDetails . filter ( function ( tx ) {
2014-03-15 17:58:28 -07:00
if ( tx . error || ! tx . result ) {
2014-03-25 15:41:30 -07:00
logger . error ( logSystem , logComponent , 'error with requesting transaction from block daemon: ' + JSON . stringify ( tx ) ) ;
2014-03-18 23:54:18 -07:00
return false ;
2014-03-15 17:58:28 -07:00
}
2014-03-18 23:54:18 -07:00
return true ;
} ) ;
2014-03-15 17:58:28 -07:00
2014-03-22 19:08:33 -07:00
var magnitude ;
rounds = rounds . filter ( function ( r ) {
2014-03-15 17:58:28 -07:00
var tx = txDetails . filter ( function ( tx ) { return tx . result . txid === r . txHash } ) [ 0 ] ;
if ( ! tx ) {
2014-03-22 23:16:06 -07:00
logger . error ( logSystem , logComponent , 'daemon did not give us back a transaction that we asked for: ' + r . txHash ) ;
2014-03-15 17:58:28 -07:00
return ;
}
r . category = tx . result . details [ 0 ] . category ;
2014-03-22 19:08:33 -07:00
if ( r . category === 'generate' ) {
2014-03-15 17:58:28 -07:00
r . amount = tx . result . amount ;
2014-03-22 19:08:33 -07:00
var roundMagnitude = r . reward / r . amount ;
if ( ! magnitude ) {
magnitude = roundMagnitude ;
if ( roundMagnitude % 10 !== 0 )
2014-03-22 23:16:06 -07:00
logger . error ( logSystem , logComponent , 'Satosihis in coin is not divisible by 10 which is very odd' ) ;
2014-03-22 19:08:33 -07:00
}
else if ( magnitude != roundMagnitude ) {
2014-03-22 23:16:06 -07:00
logger . error ( logSystem , logComponent , 'Magnitude in a round was different than in another round. HUGE PROBLEM.' ) ;
2014-03-22 19:08:33 -07:00
}
return true ;
2014-03-15 17:58:28 -07:00
}
2014-03-22 19:08:33 -07:00
else if ( r . category === 'orphan' )
return true ;
2014-03-15 17:58:28 -07:00
2014-03-11 18:56:19 -07:00
} ) ;
2014-03-12 17:09:12 -07:00
2014-03-22 19:08:33 -07:00
if ( rounds . length === 0 ) {
2014-03-22 23:46:59 -07:00
callback ( 'Check finished - no confirmed or orphaned blocks found' ) ;
2014-03-15 17:58:28 -07:00
}
else {
2014-03-22 19:08:33 -07:00
callback ( null , rounds , magnitude ) ;
2014-03-11 20:47:14 -07:00
}
2014-03-11 18:56:19 -07:00
} ) ;
} ,
2014-03-11 20:47:14 -07:00
2014-03-12 15:33:29 -07:00
/ * D o e s a b a t c h r e d i s c a l l t o g e t s h a r e s c o n t r i b u t e d t o e a c h r o u n d . T h e n c a l c u l a t e s t h e r e w a r d
amount owned to each miner for each round . * /
2014-03-22 19:08:33 -07:00
function ( rounds , magnitude , callback ) {
2014-03-11 18:56:19 -07:00
2014-03-12 15:33:29 -07:00
2014-03-12 17:09:12 -07:00
var shareLookups = rounds . map ( function ( r ) {
return [ 'hgetall' , coin + '_shares:round' + r . height ]
} ) ;
2014-03-12 15:33:29 -07:00
2014-03-22 19:08:33 -07:00
2014-03-12 17:09:12 -07:00
redisClient . multi ( shareLookups ) . exec ( function ( error , allWorkerShares ) {
2014-03-11 18:56:19 -07:00
if ( error ) {
2014-03-22 23:46:59 -07:00
callback ( 'Check finished - redis error with multi get rounds share' )
2014-03-11 18:56:19 -07:00
return ;
}
2014-03-11 20:47:14 -07:00
2014-03-22 19:08:33 -07:00
var orphanMergeCommands = [ ] ;
2014-03-12 15:33:29 -07:00
var workerRewards = { } ;
2014-03-12 17:09:12 -07:00
2014-03-22 19:08:33 -07:00
rounds . forEach ( function ( round , i ) {
var workerShares = allWorkerShares [ i ] ;
2014-03-12 15:33:29 -07:00
2014-03-22 19:08:33 -07:00
if ( round . category === 'orphan' ) {
Object . keys ( workerShares ) . forEach ( function ( worker ) {
orphanMergeCommands . push ( [ 'hincrby' , coin + '_shares:roundCurrent' , worker , workerShares [ worker ] ] ) ;
} ) ;
2014-03-11 20:47:14 -07:00
}
2014-03-22 19:08:33 -07:00
else if ( round . category === 'generate' ) {
2014-03-12 15:33:29 -07:00
2014-03-22 19:08:33 -07:00
var reward = round . reward * ( 1 - processingConfig . feePercent ) ;
2014-03-20 15:25:59 -07:00
2014-03-22 19:08:33 -07:00
var totalShares = Object . keys ( workerShares ) . reduce ( function ( p , c ) {
return p + parseInt ( workerShares [ c ] )
} , 0 ) ;
2014-03-12 15:33:29 -07:00
2014-03-22 19:08:33 -07:00
for ( var worker in workerShares ) {
var percent = parseInt ( workerShares [ worker ] ) / totalShares ;
var workerRewardTotal = Math . floor ( reward * percent ) ;
if ( ! ( worker in workerRewards ) ) workerRewards [ worker ] = 0 ;
workerRewards [ worker ] += workerRewardTotal ;
}
}
} ) ;
2014-03-12 17:09:12 -07:00
2014-03-22 19:08:33 -07:00
callback ( null , rounds , magnitude , workerRewards , orphanMergeCommands ) ;
2014-03-11 18:56:19 -07:00
} ) ;
} ,
2014-03-09 19:31:58 -07:00
2014-03-11 21:01:33 -07:00
2014-03-12 15:33:29 -07:00
/* Does a batch call to redis to get worker existing balances from coin_balances*/
2014-03-22 19:08:33 -07:00
function ( rounds , magnitude , workerRewards , orphanMergeCommands , callback ) {
2014-03-11 20:47:14 -07:00
2014-03-12 17:09:12 -07:00
var workers = Object . keys ( workerRewards ) ;
redisClient . hmget ( [ coin + '_balances' ] . concat ( workers ) , function ( error , results ) {
2014-03-22 19:08:33 -07:00
if ( error && workers . length !== 0 ) {
2014-03-22 23:46:59 -07:00
callback ( 'Check finished - redis error with multi get balances ' + JSON . stringify ( error ) ) ;
2014-03-11 20:47:14 -07:00
return ;
}
2014-03-12 23:37:27 -07:00
2014-03-11 20:47:14 -07:00
2014-03-12 17:09:12 -07:00
var workerBalances = { } ;
2014-03-11 20:47:14 -07:00
2014-03-12 17:09:12 -07:00
for ( var i = 0 ; i < workers . length ; i ++ ) {
2014-03-22 23:16:06 -07:00
workerBalances [ workers [ i ] ] = ( parseInt ( results [ i ] ) || 0 ) ;
2014-03-11 20:47:14 -07:00
}
2014-03-12 17:09:12 -07:00
2014-03-22 19:08:33 -07:00
callback ( null , rounds , magnitude , workerRewards , orphanMergeCommands , workerBalances ) ;
2014-03-11 20:47:14 -07:00
} ) ;
2014-03-12 17:09:12 -07:00
2014-03-11 20:47:14 -07:00
} ,
2014-03-11 21:01:33 -07:00
2014-03-11 20:47:14 -07:00
/ * C a l c u l a t e i f a n y p a y m e n t s a r e r e a d y t o b e s e n t a n d t r i g g e r t h e m s e n d i n g
2014-03-11 21:57:03 -07:00
Get balance different for each address and pass it along as object of latest balances such as
{ worker1 : balance1 , worker2 , balance2 }
when deciding the sent balance , it the difference should be - 1 * amount they had in db ,
if not sending the balance , the differnce should be + ( the amount they earned this round )
* /
2014-03-22 19:08:33 -07:00
function ( rounds , magnitude , workerRewards , orphanMergeCommands , workerBalances , callback ) {
//number of satoshis in a single coin unit - this can be different for coins so we calculate it :)
2014-03-20 15:25:59 -07:00
2014-03-26 08:43:52 -07:00
daemon . cmd ( 'getbalance' , [ '' ] , function ( results ) {
2014-03-20 15:25:59 -07:00
var totalBalance = results [ 0 ] . response * magnitude ;
var toBePaid = 0 ;
var workerPayments = { } ;
2014-03-11 21:01:33 -07:00
2014-03-20 15:25:59 -07:00
var balanceUpdateCommands = [ ] ;
var workerPayoutsCommand = [ ] ;
2014-03-12 17:09:12 -07:00
2014-03-20 15:25:59 -07:00
for ( var worker in workerRewards ) {
2014-03-22 19:08:33 -07:00
workerPayments [ worker ] = ( ( workerPayments [ worker ] || 0 ) + workerRewards [ worker ] ) ;
2014-03-20 15:25:59 -07:00
}
for ( var worker in workerBalances ) {
2014-03-22 19:08:33 -07:00
workerPayments [ worker ] = ( ( workerPayments [ worker ] || 0 ) + workerBalances [ worker ] ) ;
2014-03-20 15:25:59 -07:00
}
2014-03-22 19:08:33 -07:00
if ( Object . keys ( workerPayments ) . length > 0 ) {
var coinPrecision = magnitude . toString ( ) . length - 1 ;
for ( var worker in workerPayments ) {
if ( workerPayments [ worker ] < processingConfig . minimumPayment * magnitude ) {
balanceUpdateCommands . push ( [ 'hincrby' , coin + '_balances' , worker , workerRewards [ worker ] ] ) ;
delete workerPayments [ worker ] ;
}
else {
if ( workerBalances [ worker ] !== 0 ) {
balanceUpdateCommands . push ( [ 'hincrby' , coin + '_balances' , worker , - 1 * workerBalances [ worker ] ] ) ;
}
var rewardInPrecision = ( workerRewards [ worker ] / magnitude ) . toFixed ( coinPrecision ) ;
workerPayoutsCommand . push ( [ 'hincrbyfloat' , coin + '_payouts' , worker , rewardInPrecision ] ) ;
toBePaid += workerPayments [ worker ] ;
}
2014-03-20 15:25:59 -07:00
}
2014-03-22 19:08:33 -07:00
2014-03-20 15:25:59 -07:00
}
2014-03-26 16:36:07 -07:00
// txfee included in feeAmountToBeCollected
2014-03-26 12:59:59 -07:00
var feeAmountToBeCollected = parseFloat ( ( toBePaid / ( 1 - processingConfig . feePercent ) * processingConfig . feePercent ) . toFixed ( coinPrecision ) ) ;
2014-03-26 16:36:07 -07:00
var balanceLeftOver = totalBalance - toBePaid - feeAmountToBeCollected ;
2014-03-20 15:25:59 -07:00
var minReserveSatoshis = processingConfig . minimumReserve * magnitude ;
if ( balanceLeftOver < minReserveSatoshis ) {
2014-03-22 23:46:59 -07:00
callback ( 'Check finished - payments would wipe out minimum reserve, tried to pay out ' + toBePaid +
2014-03-26 16:36:07 -07:00
' and collect ' + feeAmountToBeCollected + ' as fees' +
2014-03-20 15:25:59 -07:00
' but only have ' + totalBalance + '. Left over balance would be ' + balanceLeftOver +
', needs to be at least ' + minReserveSatoshis ) ;
return ;
}
var movePendingCommands = [ ] ;
2014-03-22 19:08:33 -07:00
var roundsToDelete = [ ] ;
2014-03-20 15:25:59 -07:00
rounds . forEach ( function ( r ) {
var destinationSet = r . category === 'orphan' ? '_blocksOrphaned' : '_blocksConfirmed' ;
movePendingCommands . push ( [ 'smove' , coin + '_blocksPending' , coin + destinationSet , r . serialized ] ) ;
2014-03-22 19:08:33 -07:00
roundsToDelete . push ( coin + '_shares:round' + r . height )
2014-03-20 15:25:59 -07:00
} ) ;
var finalRedisCommands = [ ] ;
2014-03-12 17:09:12 -07:00
2014-03-22 19:08:33 -07:00
if ( movePendingCommands . length > 0 )
finalRedisCommands = finalRedisCommands . concat ( movePendingCommands ) ;
2014-03-12 17:09:12 -07:00
2014-03-22 19:08:33 -07:00
if ( orphanMergeCommands . length > 0 )
finalRedisCommands = finalRedisCommands . concat ( orphanMergeCommands ) ;
2014-03-20 15:25:59 -07:00
2014-03-22 19:08:33 -07:00
if ( balanceUpdateCommands . length > 0 )
finalRedisCommands = finalRedisCommands . concat ( balanceUpdateCommands ) ;
if ( workerPayoutsCommand . length > 0 )
finalRedisCommands = finalRedisCommands . concat ( workerPayoutsCommand ) ;
if ( roundsToDelete . length > 0 )
finalRedisCommands . push ( [ 'del' ] . concat ( roundsToDelete ) ) ;
2014-03-20 15:25:59 -07:00
2014-03-22 19:08:33 -07:00
if ( toBePaid !== 0 )
finalRedisCommands . push ( [ 'hincrbyfloat' , coin + '_stats' , 'totalPaid' , ( toBePaid / magnitude ) . toFixed ( coinPrecision ) ] ) ;
2014-03-26 16:36:07 -07:00
callback ( null , magnitude , results [ 0 ] . response , workerPayments , finalRedisCommands ) ;
2014-03-22 19:08:33 -07:00
2014-03-20 15:25:59 -07:00
} ) ;
2014-03-11 18:56:19 -07:00
} ,
2014-03-09 19:31:58 -07:00
2014-03-26 16:36:07 -07:00
function ( magnitude , balanceBefore , workerPayments , finalRedisCommands , callback ) {
2014-03-20 15:25:59 -07:00
2014-03-22 19:08:33 -07:00
//This does the final all-or-nothing atom transaction if block deamon sent payments
var finalizeRedisTx = function ( ) {
redisClient . multi ( finalRedisCommands ) . exec ( function ( error , results ) {
if ( error ) {
2014-03-22 23:46:59 -07:00
callback ( 'Check finished - error with final redis commands for cleaning up ' + JSON . stringify ( error ) ) ;
2014-03-22 19:08:33 -07:00
return ;
}
2014-03-26 17:57:29 -07:00
logger . debug ( logSystem , logComponent , 'Payments processing performed an interval' ) ;
2014-03-22 19:08:33 -07:00
} ) ;
} ;
if ( Object . keys ( workerPayments ) . length === 0 ) {
finalizeRedisTx ( ) ;
2014-03-20 15:25:59 -07:00
}
2014-03-22 19:08:33 -07:00
else {
2014-03-20 15:25:59 -07:00
2014-03-22 19:08:33 -07:00
var coinPrecision = magnitude . toString ( ) . length - 1 ;
var addressAmounts = { } ;
2014-03-22 23:16:06 -07:00
var totalAmountUnits = 0 ;
2014-03-22 19:08:33 -07:00
for ( var address in workerPayments ) {
2014-03-26 08:43:52 -07:00
var coinUnits = parseFloat ( ( workerPayments [ address ] / magnitude ) . toFixed ( coinPrecision ) ) ;
2014-03-25 18:27:28 -07:00
addressAmounts [ address ] = coinUnits ;
totalAmountUnits += coinUnits ;
2014-03-20 15:34:44 -07:00
}
2014-03-22 12:58:51 -07:00
2014-03-22 23:16:06 -07:00
logger . debug ( logSystem , logComponent , 'Payments about to be sent to: ' + JSON . stringify ( addressAmounts ) ) ;
2014-03-22 19:08:33 -07:00
daemon . cmd ( 'sendmany' , [ '' , addressAmounts ] , function ( results ) {
if ( results [ 0 ] . error ) {
2014-03-22 23:46:59 -07:00
callback ( 'Check finished - error with sendmany ' + JSON . stringify ( results [ 0 ] . error ) ) ;
2014-03-20 15:34:44 -07:00
return ;
}
2014-03-22 19:08:33 -07:00
finalizeRedisTx ( ) ;
2014-03-22 12:58:51 -07:00
var totalWorkers = Object . keys ( workerPayments ) . length ;
2014-03-26 16:36:07 -07:00
logger . debug ( logSystem , logComponent , 'Payments sent, a total of ' + totalAmountUnits + ' ' + poolOptions . coin . symbol +
2014-03-22 19:08:33 -07:00
' was sent to ' + totalWorkers + ' miners' ) ;
2014-03-26 16:36:07 -07:00
setTimeout ( function ( ) { // not sure if we need some time to let daemon update the wallet balance
daemon . cmd ( 'getbalance' , [ '' ] , function ( results ) {
var balanceDiff = balanceBefore - results [ 0 ] . response ;
var txFee = balanceDiff - totalAmountUnits ;
var feeAmountUnits = parseFloat ( ( totalAmountUnits / ( 1 - processingConfig . feePercent ) * processingConfig . feePercent ) . toFixed ( coinPrecision ) ) ;
var poolFees = feeAmountUnits - txFee ;
daemon . cmd ( 'move' , [ '' , processingConfig . feeCollectAccount , poolFees ] , function ( results ) {
if ( results [ 0 ] . error ) {
callback ( 'Check finished - error with move ' + JSON . stringify ( results [ 0 ] . error ) ) ;
return ;
}
callback ( null , poolFees + ' ' + poolOptions . coin . symbol + ' collected as pool fee' ) ;
} ) ;
} ) ;
} , 1000 ) ;
2014-03-20 15:34:44 -07:00
} ) ;
2014-03-22 19:08:33 -07:00
}
2014-03-11 18:56:19 -07:00
}
] , function ( error , result ) {
2014-03-20 15:34:44 -07:00
if ( error )
2014-03-22 23:16:06 -07:00
logger . debug ( logSystem , logComponent , error ) ;
2014-03-20 15:34:44 -07:00
else {
2014-03-22 23:16:06 -07:00
logger . debug ( logSystem , logComponent , result ) ;
2014-03-26 16:36:07 -07:00
// not sure if we need some time to let daemon update the wallet balance
setTimeout ( withdrawalProfit , 1000 ) ;
2014-03-20 15:34:44 -07:00
}
2014-03-09 19:31:58 -07:00
} ) ;
2014-03-11 18:56:19 -07:00
} ;
2014-03-20 16:05:13 -07:00
var withdrawalProfit = function ( ) {
2014-03-20 16:11:39 -07:00
if ( ! processingConfig . feeWithdrawalThreshold ) return ;
2014-03-26 16:49:08 -07:00
logger . debug ( logSystem , logComponent , 'Profit withdrawal started' ) ;
2014-03-26 08:43:52 -07:00
daemon . cmd ( 'getbalance' , [ processingConfig . feeCollectAccount ] , function ( results ) {
2014-03-20 16:05:13 -07:00
2014-03-26 16:36:07 -07:00
// We have to pay some tx fee here too but maybe we shoudn't really care about it too much as long as fee is less
// then minimumReserve value. Because in this case even if feeCollectAccount account will have negative balance
// total wallet balance will be positive and feeCollectAccount account will be refilled during next payment processing.
// But to be as much accurate as we can we use getinfo command to retrieve minimum tx fee (paytxfee).
daemon . cmd ( 'getinfo' , [ ] , function ( result ) {
var paytxfee ;
if ( ! result [ 0 ] . response || ! result [ 0 ] . response . paytxfee ) {
logger . error ( logSystem , logComponent , 'Daemon does not have paytxfee property on getinfo method results - withdrawal processing could be broken with this daemon' ) ;
paytxfee = 0 ;
} else {
paytxfee = result [ 0 ] . response . paytxfee ;
}
2014-03-20 16:05:13 -07:00
2014-03-26 16:36:07 -07:00
var withdrawalAmount = results [ 0 ] . response - paytxfee ;
2014-03-26 08:43:52 -07:00
2014-03-26 16:36:07 -07:00
if ( withdrawalAmount < processingConfig . feeWithdrawalThreshold ) {
2014-03-26 16:49:08 -07:00
logger . debug ( logSystem , logComponent , 'Not enough profit to withdraw yet' ) ;
2014-03-26 16:36:07 -07:00
}
else {
2014-03-26 08:43:52 -07:00
2014-03-26 16:36:07 -07:00
var withdrawal = { } ;
withdrawal [ processingConfig . feeReceiveAddress ] = withdrawalAmount ;
daemon . cmd ( 'sendmany' , [ processingConfig . feeCollectAccount , withdrawal ] , function ( results ) {
if ( results [ 0 ] . error ) {
2014-03-26 16:49:08 -07:00
logger . debug ( logSystem , logComponent , 'Profit withdrawal finished - error with sendmany ' + JSON . stringify ( results [ 0 ] . error ) ) ;
2014-03-26 16:36:07 -07:00
return ;
}
logger . debug ( logSystem , logComponent , 'Profit sent, a total of ' + withdrawalAmount + ' ' + poolOptions . coin . symbol +
' was sent to ' + processingConfig . feeReceiveAddress ) ;
} ) ;
}
} ) ;
2014-03-20 16:05:13 -07:00
} ) ;
2014-03-20 16:11:39 -07:00
2014-03-20 16:05:13 -07:00
} ;
2014-03-11 20:47:14 -07:00
setInterval ( processPayments , processingConfig . paymentInterval * 1000 ) ;
setTimeout ( processPayments , 100 ) ;
2014-03-09 19:31:58 -07:00
} ;