Skip to content

Calling external function from async.waterfall

I’m somewhat new to JS and working with an existing forked library (which is very complicated and poorly documented) – so please bear with me.

I’m working on a mining pool system, and specifically adding a feature where users can set a custom payout amount instead of the lower pool limit. I’ve already sorted writing that value for a users worker address into redis.

What I’m trying to do now is add logic that will:

  1. Use the worker Address that did work to see if they set a custom payout value (my external function below)
  2. If it’s set, some logic to determine if they should be paid immediately or add it to their balance

The existing waterfall script is here: https://github.com/mardock2009/Ravencoin-Pool/blob/master/libs/paymentProcessor.js#L575

Before the waterfall, i’m adding a function similar to this (i’ve tried many iterations):

getCustomPayoutAmount = function(worker, cback) {
    var logger = loggerFactory.getLogger('PaymentProcessing', 'getCustomPayoutAmount');

    var redisClient = redis.createClient(portalConfig.redis.port, portalConfig.redis.host);             
    logger.debug('Getting custom Payout for worker: %s', worker);
    var payoutAmount = new BigNumber(0);

    redisClient.hget('ravencoin:workers:customPayoutAmount', worker,  function(error, result) {
        if (error) {
            logger.error('Error getCustomPayoutAmount: %s', error);
            payoutAmount = 0;
        }
        logger.debug('Got custom payout amount for worker: %s, Payout Amount: %s', worker, result);
        if (result > 10) {
            payoutAmount = new BigNumber(result);
            logger.debug('Parsed Float Amount: %s', payoutAmount);
        } else {
            logger.debug('Else lower than: %s', payoutAmount);
            payoutAmount = new BigNumber(0);
        }       
    });
    cback( new BigNumber(payoutAmount));    
};

Then at around line 575, i’m calling it (maybe?):

                    var customPayoutAmount = new BigNumber(0);

                    getCustomPayoutAmount(worker, function(returnCustomPayoutAmount) {
                        logger.debug('Callback customPayoutAmount = %s', returnCustomPayoutAmount);
                        customPayoutAmount = returnCustomPayoutAmount;
                    });

                    logger.debug('PP> customPayoutAmount = %s', customPayoutAmount);

And then finally, some if/elseif logic to handle the different cases:

if (toSend.isGreaterThanOrEqualTo(minPayment)) {

                        if (toSend.isGreaterThanOrEqualTo(customPayoutAmount) && !customPayoutAmount.isZero()) {
                            //Amount Sent is higher than the custom amount set for this worker. Pay it out.
                            logger.debug('PP> Worker %s have their custom minimum payout amount: (%s above minimum %s)', w, toSend.toString(10), customPayoutAmount.toString(10));
                            totalSent = totalSent.plus(toSend);              
                            logger.debug('PP> totalSent = %s', totalSent.toString(10));
                            var address = worker.address = (worker.address || getProperAddress(w));              
                            logger.debug('PP> address = %s', address);
                            worker.sent = addressAmounts[address] = toSend;
                            logger.debug('PP> worker.sent = %s', worker.sent.toString(10));
                            worker.balanceChange = BigNumber.min(worker.balance, worker.sent).multipliedBy(new BigNumber(-1));
                            logger.debug('PP> worker.balanceChange = %s', worker.balanceChange.toString(10));

                        } else if (toSend.isLessThan(customPayoutAmount) && !customPayoutAmount.isZero()){
                            //Amount is higher than the minimum payment but not higher than the custom amount set for this worker. Add it to their balance.
                            //Did not meet the pool minimum, no custom amount. Add to balance.
                            logger.debug('PP> Worker %s have not reached minimum payout from their custom set payout amount threshold %s', w, customPayoutAmount.toString(10));
                            worker.balanceChange = BigNumber.max(toSend.minus(worker.balance), new BigNumber(0));
                            logger.debug('PP> worker.balanceChange = %s', worker.balanceChange.toString(10));
                            worker.sent = new BigNumber(0);
                            logger.debug('PP> worker.sent = %s', worker.sent.toString(10));
                            if (worker.balanceChange > 0) {
                                if (balanceAmounts[address] != null && balanceAmounts[address].isGreaterThan(0)) {
                                    balanceAmounts[address] = balanceAmounts[address].plus(worker.balanceChange);
                                } else {
                                    balanceAmounts[address] = worker.balanceChange;
                                }
                            }
                        }
                        
                        if (toSend.isGreaterThanOrEqualTo(minPayment) && customPayoutAmount.isZero()) {
                            //Meets the pool minimum payment, no custom amount. Pay out based on the pool minimum payment.
                            logger.debug('PP> Worker %s have reached minimum payout threshold (%s above minimum %s)', w, toSend.toString(10), minPayment.toString(10));
                            totalSent = totalSent.plus(toSend);              
                            logger.debug('PP> totalSent = %s', totalSent.toString(10));
                            var address = worker.address = (worker.address || getProperAddress(w));              
                            logger.debug('PP> address = %s', address);
                            worker.sent = addressAmounts[address] = toSend;
                            logger.debug('PP> worker.sent = %s', worker.sent.toString(10));
                            worker.balanceChange = BigNumber.min(worker.balance, worker.sent).multipliedBy(new BigNumber(-1));
                            logger.debug('PP> worker.balanceChange = %s', worker.balanceChange.toString(10));
                        }

                        
                    } else {
                        //Did not meet the pool minimum, no custom amount. Add to balance.
                        logger.debug('PP> Worker %s have not reached minimum payout threshold %s', w, minPayment.toString(10));
                        worker.balanceChange = BigNumber.max(toSend.minus(worker.balance), new BigNumber(0));
                        logger.debug('PP> worker.balanceChange = %s', worker.balanceChange.toString(10));
                        worker.sent = new BigNumber(0);
                        logger.debug('PP> worker.sent = %s', worker.sent.toString(10));
                        if (worker.balanceChange > 0) {
                            if (balanceAmounts[address] != null && balanceAmounts[address].isGreaterThan(0)) {
                                balanceAmounts[address] = balanceAmounts[address].plus(worker.balanceChange);
                            } else {
                                balanceAmounts[address] = worker.balanceChange;
                            }
                        }
                    }

The main issue I’m having is I cant get values back from redis calling it inside the waterfall. I’m assuming because it’s async andIi’m not writing async code – which also causes it to not necessarily run in order when I need the values.

Again, I know this is a mess and I’m probably going about this all wrong, so hopefully someone has some insight for this poor noob.

Answer

You can use async.each()

function(workers, rounds, addressAccount, callback) {
  var trySend = function(withholdPercent) {
    ...
    async.each(workers, function(worker, callback) {
      ...
      getCustomPayoutAmount(worker, function(customPayoutAmount) {
        ...
        if (toSend.isGreaterThanOrEqualTo(minPayment)) {
          if (toSend.isGreaterThanOrEqualTo(customPayoutAmount) && !customPayoutAmount.isZero()) {
            ...
          }
          ...
        }
        callback();
      });
    })
    .then(function() {
      if (Object.keys(addressAmounts).length === 0) {
        logger.info('PP> No workers was chosen for paying out');
        callback(null, workers, rounds, []);
        return;
      }
      ...
      daemon.cmd('sendmany', [addressAccount || '', addressAmounts, 1, ""], function(result) {
        ...
      }
    });
  };
  trySend(new BigNumber(0);
}

and In ‘getCustomPayoutAmount’, ‘cback()` should be called within the callback of ‘redisClient.hget()’ like below

getCustomPayoutAmount = function(worker, cback) {
    ...

    redisClient.hget('ravencoin:workers:customPayoutAmount', worker,  function(error, result) {
        ...
        cback(payoutAmount);   
    });
     
};