From 0e49e8440d785f7ad7c13cf165505329e617d954 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sun, 9 Feb 2014 09:45:20 +0100 Subject: [PATCH 1/7] [ADDED] First draft: sendmany RPC support We can now run payouts via sendmany: * No big logical changes, sendmany is called once for MP and once for AP * Instead of paying out via sendtoaddress, store in array and use sendmany later * Update all transactions with the RPC TXID returned by sendmany Will fix #1238 once merged. --- cronjobs/payouts.php | 60 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/cronjobs/payouts.php b/cronjobs/payouts.php index cd16c68d..eabfa502 100755 --- a/cronjobs/payouts.php +++ b/cronjobs/payouts.php @@ -34,13 +34,24 @@ if ($bitcoin->can_connect() !== true) { $log->logFatal(" unable to connect to RPC server, exiting"); $monitoring->endCronjob($cron_name, 'E0006', 1, true); } + +// Check and see if the sendmany RPC method is available +// This does not test if it actually works too! +$sendmanyAvailable = ((strpos($bitcoin->help('sendmany'), 'unknown') === FALSE) ? true : false); +if ($sendmanyAvailable) + $log->logInfo(' sendmany available in coind help command'); + if (!$dWalletBalance = $bitcoin->getbalance()) $dWalletBalance = 0; +// Fetch outstanding manual-payouts +$aManualPayouts = $transaction->getMPQueue(); + // Fetch our manual payouts, process them -if ($setting->getValue('disable_manual_payouts') != 1 && $aManualPayouts = $transaction->getMPQueue()) { +if ($setting->getValue('disable_manual_payouts') != 1 && $aManualPayouts) { // Calculate our sum first $dMPTotalAmount = 0; + $aSendMany = NULL; foreach ($aManualPayouts as $aUserData) $dMPTotalAmount += $aUserData['confirmed']; if ($dMPTotalAmount > $dWalletBalance) { $log->logError(" Wallet does not cover MP payouts"); @@ -62,7 +73,7 @@ if ($setting->getValue('disable_manual_payouts') != 1 && $aManualPayouts = $tran if (!$transaction_id = $transaction->createDebitMPRecord($aUserData['id'], $aUserData['coin_address'], $aUserData['confirmed'] - $config['txfee_manual'])) { $log->logFatal(' failed to fullt debit user ' . $aUserData['username'] . ': ' . $transaction->getCronError()); $monitoring->endCronjob($cron_name, 'E0064', 1, true); - } else { + } else if (!$sendmanyAvailable) { // Run the payouts from RPC now that the user is fully debited try { $rpc_txid = $bitcoin->sendtoaddress($aUserData['coin_address'], $aUserData['confirmed'] - $config['txfee_manual']); @@ -75,18 +86,41 @@ if ($setting->getValue('disable_manual_payouts') != 1 && $aManualPayouts = $tran // Update our transaction and add the RPC Transaction ID if (empty($rpc_txid) || !$transaction->setRPCTxId($transaction_id, $rpc_txid)) $log->logError('Unable to add RPC transaction ID ' . $rpc_txid . ' to transaction record ' . $transaction_id . ': ' . $transaction->getCronError()); + } else { + // We don't run sendtoaddress but run sendmany later + $aSendMany[$aUserData['coin_address']] = $aUserData['confirmed']; + $aTransactions[] = $transaction_id; } } else { $log->logInfo(' failed to validate address for user: ' . $aUserData['username']); continue; } } + if ($sendmanyAvailable && is_array($aSendMany)) { + try { + $rpc_txid = $bitcoin->sendmany('', $aSendMany); + } catch (Exception $e) { + $log->logError('E0078: RPC method sendmany did not return 200 OK: Address: ' . $aUserData['coin_address'] . ' ERROR: ' . $e->getMessage()); + // Remove this line below if RPC calls are failing but transactions are still added to it + // Don't blame MPOS if you run into issues after commenting this out! + $monitoring->endCronjob($cron_name, 'E0078', 1, true); + } + foreach ($aTransactions as $iTransactionID) { + if (empty($rpc_txid) || !$transaction->setRPCTxId($iTransactionID, $rpc_txid)) + $log->logError('Unable to add RPC transaction ID ' . $rpc_txid . ' to transaction record ' . $iTransactionID . ': ' . $transaction->getCronError()); + } + } } if (!$dWalletBalance = $bitcoin->getbalance()) $dWalletBalance = 0; + +// Fetch outstanding auto-payouts +$aAutoPayouts = $transaction->getAPQueue(); + // Fetch our auto payouts, process them -if ($setting->getValue('disable_auto_payouts') != 1 && $aAutoPayouts = $transaction->getAPQueue()) { +if ($setting->getValue('disable_auto_payouts') != 1 && $aAutoPayouts) { + $aSendMany = NULL; // Calculate our sum first $dAPTotalAmount = 0; foreach ($aAutoPayouts as $aUserData) $dAPTotalAmount += $aUserData['confirmed']; @@ -106,7 +140,7 @@ if ($setting->getValue('disable_auto_payouts') != 1 && $aAutoPayouts = $transact if (!$transaction_id = $transaction->createDebitAPRecord($aUserData['id'], $aUserData['coin_address'], $aUserData['confirmed'] - $config['txfee_manual'])) { $log->logFatal(' failed to fully debit user ' . $aUserData['username'] . ': ' . $transaction->getCronError()); $monitoring->endCronjob($cron_name, 'E0064', 1, true); - } else { + } else if (!$sendmanyAvailable) { // Run the payouts from RPC now that the user is fully debited try { $rpc_txid = $bitcoin->sendtoaddress($aUserData['coin_address'], $aUserData['confirmed'] - $config['txfee_manual']); @@ -119,12 +153,30 @@ if ($setting->getValue('disable_auto_payouts') != 1 && $aAutoPayouts = $transact // Update our transaction and add the RPC Transaction ID if (empty($rpc_txid) || !$transaction->setRPCTxId($transaction_id, $rpc_txid)) $log->logError('Unable to add RPC transaction ID ' . $rpc_txid . ' to transaction record ' . $transaction_id . ': ' . $transaction->getCronError()); + } else { + // We don't run sendtoaddress but run sendmany later + $aSendMany[$aUserData['coin_address']] = $aUserData['confirmed']; + $aTransactions[] = $transaction_id; } } else { $log->logInfo(' failed to validate address for user: ' . $aUserData['username']); continue; } } + if ($sendmanyAvailable && is_array($aSendMany)) { + try { + $rpc_txid = $bitcoin->sendmany('', $aSendMany); + } catch (Exception $e) { + $log->logError('E0078: RPC method sendmany did not return 200 OK: Address: ' . $aUserData['coin_address'] . ' ERROR: ' . $e->getMessage()); + // Remove this line below if RPC calls are failing but transactions are still added to it + // Don't blame MPOS if you run into issues after commenting this out! + $monitoring->endCronjob($cron_name, 'E0078', 1, true); + } + foreach ($aTransactions as $iTransactionID) { + if (empty($rpc_txid) || !$transaction->setRPCTxId($iTransactionID, $rpc_txid)) + $log->logError('Unable to add RPC transaction ID ' . $rpc_txid . ' to transaction record ' . $iTransactionID . ': ' . $transaction->getCronError()); + } + } } require_once('cron_end.inc.php'); From 4359b6a6491dc7e66e7c564586c2d62ccfd33588 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Sun, 9 Feb 2014 17:11:58 +0100 Subject: [PATCH 2/7] [CHANGE] Made startup messages debug level --- cronjobs/payouts.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cronjobs/payouts.php b/cronjobs/payouts.php index eabfa502..bfb7c7b4 100755 --- a/cronjobs/payouts.php +++ b/cronjobs/payouts.php @@ -29,7 +29,7 @@ if ($setting->getValue('disable_payouts') == 1) { $log->logInfo(" payouts disabled via admin panel"); $monitoring->endCronjob($cron_name, 'E0009', 0, true, false); } -$log->logInfo("Starting Payout..."); +$log->logDebug("Starting Payout..."); if ($bitcoin->can_connect() !== true) { $log->logFatal(" unable to connect to RPC server, exiting"); $monitoring->endCronjob($cron_name, 'E0006', 1, true); @@ -39,7 +39,7 @@ if ($bitcoin->can_connect() !== true) { // This does not test if it actually works too! $sendmanyAvailable = ((strpos($bitcoin->help('sendmany'), 'unknown') === FALSE) ? true : false); if ($sendmanyAvailable) - $log->logInfo(' sendmany available in coind help command'); + $log->logDebug(' sendmany available in coind help command'); if (!$dWalletBalance = $bitcoin->getbalance()) $dWalletBalance = 0; From 332aa5aaa4dd620a2108c8b279406739f6a57cfa Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Wed, 12 Feb 2014 10:20:54 +0100 Subject: [PATCH 3/7] [FIX] Apply TXFees before running sendmany --- cronjobs/payouts.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cronjobs/payouts.php b/cronjobs/payouts.php index bfb7c7b4..c8d9fd8b 100755 --- a/cronjobs/payouts.php +++ b/cronjobs/payouts.php @@ -88,7 +88,7 @@ if ($setting->getValue('disable_manual_payouts') != 1 && $aManualPayouts) { $log->logError('Unable to add RPC transaction ID ' . $rpc_txid . ' to transaction record ' . $transaction_id . ': ' . $transaction->getCronError()); } else { // We don't run sendtoaddress but run sendmany later - $aSendMany[$aUserData['coin_address']] = $aUserData['confirmed']; + $aSendMany[$aUserData['coin_address']] = $aUserData['confirmed'] - $config['txfee_manual']; $aTransactions[] = $transaction_id; } } else { @@ -155,7 +155,7 @@ if ($setting->getValue('disable_auto_payouts') != 1 && $aAutoPayouts) { $log->logError('Unable to add RPC transaction ID ' . $rpc_txid . ' to transaction record ' . $transaction_id . ': ' . $transaction->getCronError()); } else { // We don't run sendtoaddress but run sendmany later - $aSendMany[$aUserData['coin_address']] = $aUserData['confirmed']; + $aSendMany[$aUserData['coin_address']] = $aUserData['confirmed'] - $config['txfee_auto']; $aTransactions[] = $transaction_id; } } else { From 71aca7e0afb1ff651705265db7bac4cba0a10368 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Wed, 12 Feb 2014 12:55:41 +0100 Subject: [PATCH 4/7] [UPDATE] Getbalance wrapper We need to wrap our getbalance call due to issues in the coind and payout systems in the RPC. Apparently it's having issues with multiple accounts setup in a single wallet. Sendmany can not properly use other accounts added to the wallet as the payout account. Hence we wrap our getbalance call in the RPC and only return the default account balance at all times. Since this is used for all payouts and for payments from blocks, there should be no issue. Those pools running multiple accounts in their wallet will have to move their coins into the default wallet! --- public/include/classes/bitcoinwrapper.class.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/public/include/classes/bitcoinwrapper.class.php b/public/include/classes/bitcoinwrapper.class.php index ccab6b41..ebe1c470 100644 --- a/public/include/classes/bitcoinwrapper.class.php +++ b/public/include/classes/bitcoinwrapper.class.php @@ -31,6 +31,12 @@ class BitcoinWrapper extends BitcoinClient { if ($data = $this->memcache->get(__FUNCTION__)) return $data; return $this->memcache->setCache(__FUNCTION__, parent::getmininginfo(), 30); } + // Wrapper to check our wallet balance from the DEFAULT account only + public function getbalance() { + $this->oDebug->append("STA " . __METHOD__, 4); + $aAccounts = parent::listaccounts(); + return $aAccounts['']; + } public function getblockcount() { $this->oDebug->append("STA " . __METHOD__, 4); if ($data = $this->memcache->get(__FUNCTION__)) return $data; From d725f9bf5d4b10a177471c4b256bd9b26090528e Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Wed, 12 Feb 2014 15:53:57 +0100 Subject: [PATCH 5/7] [ADDED] Sendmany config option * Disable sendmany by default * Allow people to enable the new feature via config --- cronjobs/payouts.php | 8 ++++---- public/include/config/global.inc.dist.php | 7 +++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/cronjobs/payouts.php b/cronjobs/payouts.php index c8d9fd8b..ea3ea1e2 100755 --- a/cronjobs/payouts.php +++ b/cronjobs/payouts.php @@ -73,7 +73,7 @@ if ($setting->getValue('disable_manual_payouts') != 1 && $aManualPayouts) { if (!$transaction_id = $transaction->createDebitMPRecord($aUserData['id'], $aUserData['coin_address'], $aUserData['confirmed'] - $config['txfee_manual'])) { $log->logFatal(' failed to fullt debit user ' . $aUserData['username'] . ': ' . $transaction->getCronError()); $monitoring->endCronjob($cron_name, 'E0064', 1, true); - } else if (!$sendmanyAvailable) { + } else if (!$config['sendmany']['enabled'] || !$sendmanyAvailable) { // Run the payouts from RPC now that the user is fully debited try { $rpc_txid = $bitcoin->sendtoaddress($aUserData['coin_address'], $aUserData['confirmed'] - $config['txfee_manual']); @@ -96,7 +96,7 @@ if ($setting->getValue('disable_manual_payouts') != 1 && $aManualPayouts) { continue; } } - if ($sendmanyAvailable && is_array($aSendMany)) { + if ($config['sendmany']['enabled'] && $sendmanyAvailable && is_array($aSendMany)) { try { $rpc_txid = $bitcoin->sendmany('', $aSendMany); } catch (Exception $e) { @@ -140,7 +140,7 @@ if ($setting->getValue('disable_auto_payouts') != 1 && $aAutoPayouts) { if (!$transaction_id = $transaction->createDebitAPRecord($aUserData['id'], $aUserData['coin_address'], $aUserData['confirmed'] - $config['txfee_manual'])) { $log->logFatal(' failed to fully debit user ' . $aUserData['username'] . ': ' . $transaction->getCronError()); $monitoring->endCronjob($cron_name, 'E0064', 1, true); - } else if (!$sendmanyAvailable) { + } else if (!$config['sendmany']['enabled'] || !$sendmanyAvailable) { // Run the payouts from RPC now that the user is fully debited try { $rpc_txid = $bitcoin->sendtoaddress($aUserData['coin_address'], $aUserData['confirmed'] - $config['txfee_manual']); @@ -163,7 +163,7 @@ if ($setting->getValue('disable_auto_payouts') != 1 && $aAutoPayouts) { continue; } } - if ($sendmanyAvailable && is_array($aSendMany)) { + if ($config['sendmany']['enabled'] && $sendmanyAvailable && is_array($aSendMany)) { try { $rpc_txid = $bitcoin->sendmany('', $aSendMany); } catch (Exception $e) { diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index 66ea0e51..52c07ac5 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -144,6 +144,13 @@ $config['block_bonus'] = 0; **/ $config['payout_system'] = 'prop'; +/** + * Sendmany Support + * Enable/Disable Sendmany RPC method + * tba + **/ +$config['sendmany']['enabled'] = false; + /** * Round Purging * Round share purging configuration From eb40e4080a526b5912c8f935badbe5898546bb40 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Wed, 12 Feb 2014 16:20:07 +0100 Subject: [PATCH 6/7] [ADDED] Log RPC TXID into logfile for payouts --- cronjobs/payouts.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cronjobs/payouts.php b/cronjobs/payouts.php index ea3ea1e2..c96af16c 100755 --- a/cronjobs/payouts.php +++ b/cronjobs/payouts.php @@ -105,6 +105,7 @@ if ($setting->getValue('disable_manual_payouts') != 1 && $aManualPayouts) { // Don't blame MPOS if you run into issues after commenting this out! $monitoring->endCronjob($cron_name, 'E0078', 1, true); } + $log->logInfo(' payout succeeded with RPC TXID: ' . $rpc_txid); foreach ($aTransactions as $iTransactionID) { if (empty($rpc_txid) || !$transaction->setRPCTxId($iTransactionID, $rpc_txid)) $log->logError('Unable to add RPC transaction ID ' . $rpc_txid . ' to transaction record ' . $iTransactionID . ': ' . $transaction->getCronError()); @@ -172,6 +173,7 @@ if ($setting->getValue('disable_auto_payouts') != 1 && $aAutoPayouts) { // Don't blame MPOS if you run into issues after commenting this out! $monitoring->endCronjob($cron_name, 'E0078', 1, true); } + $log->logInfo(' payout succeeded with RPC TXID: ' . $rpc_txid); foreach ($aTransactions as $iTransactionID) { if (empty($rpc_txid) || !$transaction->setRPCTxId($iTransactionID, $rpc_txid)) $log->logError('Unable to add RPC transaction ID ' . $rpc_txid . ' to transaction record ' . $iTransactionID . ': ' . $transaction->getCronError()); From 3d10f25d226da5c45c235dac600e9223d5e115cc Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Thu, 13 Feb 2014 09:01:45 +0100 Subject: [PATCH 7/7] [ADDED] Documentation URL --- public/include/config/global.inc.dist.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index 52c07ac5..fb7e22e2 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -147,7 +147,7 @@ $config['payout_system'] = 'prop'; /** * Sendmany Support * Enable/Disable Sendmany RPC method - * tba + * https://github.com/MPOS/php-mpos/wiki/Config-Setup#wiki-sendmany-support **/ $config['sendmany']['enabled'] = false;