diff --git a/include/autoloader.inc.php b/include/autoloader.inc.php index 4d14e17e5..eb44f86f0 100644 --- a/include/autoloader.inc.php +++ b/include/autoloader.inc.php @@ -16,6 +16,7 @@ // We need to load these first require_once(CLASS_DIR . '/base.class.php'); require_once(CLASS_DIR . '/coins/coin_base.class.php'); +require_once(CLASS_DIR . '/coin_address.class.php'); require_once(CLASS_DIR . '/setting.class.php'); require_once(INCLUDE_DIR . '/version.inc.php'); if (PHP_OS == 'WINNT') require_once(CLASS_DIR . '/memcached.class.php'); diff --git a/include/classes/base.class.php b/include/classes/base.class.php index 1bb27e3ac..d9f950add 100644 --- a/include/classes/base.class.php +++ b/include/classes/base.class.php @@ -22,6 +22,9 @@ public function setDebug($debug) { public function setCoin($coin) { $this->coin = $coin; } + public function setCoinAddress($coin_address) { + $this->coin_address = $coin_address; + } public function setLog($log) { $this->log = $log; } diff --git a/include/classes/coin_address.class.php b/include/classes/coin_address.class.php new file mode 100644 index 000000000..e5f108ac8 --- /dev/null +++ b/include/classes/coin_address.class.php @@ -0,0 +1,92 @@ +config['currency']; + $this->debug->append("STA " . __METHOD__, 4); + $stmt = $this->mysqli->prepare(" + SELECT coin_address + FROM " . $this->getTableName() . " + WHERE account_id = ? AND currency = ? + "); + if ( $this->checkStmt($stmt) && $stmt->bind_param('is', $userID, $currency) && $stmt->execute() && $result = $stmt->get_result()) { + if ($result->num_rows == 1) { + return $result->fetch_object()->coin_address; + } + } + $this->debug->append("Unable to fetch users coin address for " . $currency); + return $this->sqlError(); + } + + /** + * Check if a coin address is already set + * @param address string Coin Address to check for + * @return bool true or false + **/ + public function existsCoinAddress($address) { + $this->debug->append("STA " . __METHOD__, 4); + return $this->getSingle($address, 'coin_address', 'coin_address', 's') === $address; + } + + /** + * Add a new coin address record for a user + * @param userID int Account ID + * @param address string Coin Address + * @param currency string Currency short handle, defaults to config option + * @return bool true or false + **/ + public function add($userID, $address, $currency=NULL) { + if ($currency === NULL) $currency = $this->config['currency']; + if ($address != $this->getCoinAddress($userID) && $this->existsCoinAddress($address)) { + $this->setErrorMessage('Unable to update coin address, address already exists'); + return false; + } + $stmt = $this->mysqli->prepare("INSERT INTO " . $this->getTableName() . " (account_id, currency, coin_address) VALUES (?, ?, ?)"); + if ( $this->checkStmt($stmt) && $stmt->bind_param('sis', $userID, $currency, $address) && $stmt->execute()) { + return true; + } + return $this->sqlError(); + } + + /** + * Update a coin address record for a user and a currency + * @param userID int Account ID + * @param address string Coin Address + * @param currency string Currency short handle, defaults to config option + * @return bool true or false + **/ + public function update($userID, $address, $currency=NULL) { + if ($currency === NULL) $currency = $this->config['currency']; + if ($address != $this->getCoinAddress($userID) && $this->existsCoinAddress($address)) { + $this->setErrorMessage('Unable to update coin address, address already exists'); + return false; + } + if ($this->getCoinAddress($userID) != NULL) { + $stmt = $this->mysqli->prepare("UPDATE " . $this->getTableName() . " SET coin_address = ? WHERE account_id = ? AND currency = ?"); + if ( $this->checkStmt($stmt) && $stmt->bind_param('sis', $address, $userID, $currency) && $stmt->execute()) { + return true; + } + } else { + $stmt = $this->mysqli->prepare("INSERT INTO " . $this->getTableName() . " (coin_address, account_id, currency) VALUES (?, ?, ?)"); + if ( $this->checkStmt($stmt) && $stmt->bind_param('sis', $address, $userID, $currency) && $stmt->execute()) { + return true; + } + } + return $this->sqlError(); + } +} + +$coin_address = new CoinAddress(); +$coin_address->setDebug($debug); +$coin_address->setConfig($config); +$coin_address->setMysql($mysqli); +$coin_address->setErrorCodes($aErrorCodes); diff --git a/include/classes/transaction.class.php b/include/classes/transaction.class.php index d01b53e34..8920419b1 100644 --- a/include/classes/transaction.class.php +++ b/include/classes/transaction.class.php @@ -355,7 +355,7 @@ public function getAPQueue($limit=250) { a.id, a.username, a.ap_threshold, - a.coin_address, + ca.coin_address, IFNULL( ROUND( ( @@ -370,11 +370,13 @@ public function getAPQueue($limit=250) { ON t.block_id = b.id LEFT JOIN " . $this->user->getTableName() . " AS a ON t.account_id = a.id - WHERE t.archived = 0 AND a.ap_threshold > 0 AND a.coin_address IS NOT NULL AND a.coin_address != '' + LEFT JOIN " . $this->coin_address->getTableName() . " AS ca + ON ca.account_id = a.id + WHERE t.archived = 0 AND a.ap_threshold > 0 AND ca.coin_address IS NOT NULL AND ca.coin_address != '' AND ca.currency = ? GROUP BY t.account_id HAVING confirmed > a.ap_threshold AND confirmed > " . $this->config['txfee_auto'] . " LIMIT ?"); - if ($this->checkStmt($stmt) && $stmt->bind_param('i', $limit) && $stmt->execute() && $result = $stmt->get_result()) + if ($this->checkStmt($stmt) && $stmt->bind_param('si', $this->config['currency'], $limit) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_all(MYSQLI_ASSOC); return $this->sqlError(); } @@ -446,7 +448,7 @@ public function getMPQueue($limit=250) { a.id, a.username, a.ap_threshold, - a.coin_address, + ca.coin_address, p.id AS payout_id, IFNULL( ROUND( @@ -464,11 +466,13 @@ public function getMPQueue($limit=250) { ON t.account_id = p.account_id LEFT JOIN " . $this->block->getTableName() . " AS b ON t.block_id = b.id - WHERE p.completed = 0 AND t.archived = 0 AND a.coin_address IS NOT NULL AND a.coin_address != '' + LEFT JOIN " . $this->coin_address->getTableName() . " AS ca + ON ca.account_id = a.id + WHERE p.completed = 0 AND t.archived = 0 AND ca.currency = ? AND ca.coin_address IS NOT NULL AND ca.coin_address != '' GROUP BY t.account_id HAVING confirmed > " . $this->config['txfee_manual'] . " LIMIT ?"); - if ($this->checkStmt($stmt) && $stmt->bind_param('i', $limit) && $stmt->execute() && $result = $stmt->get_result()) + if ($this->checkStmt($stmt) && $stmt->bind_param('si', $this->config['currency'], $limit) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_all(MYSQLI_ASSOC); return $this->sqlError('E0050'); } @@ -478,6 +482,7 @@ public function getMPQueue($limit=250) { $transaction->setMemcache($memcache); $transaction->setNotification($notification); $transaction->setDebug($debug); +$transaction->setCoinAddress($coin_address); $transaction->setMysql($mysqli); $transaction->setConfig($config); $transaction->setBlock($block); diff --git a/include/classes/user.class.php b/include/classes/user.class.php index 7cb0d58bd..5e59b8534 100644 --- a/include/classes/user.class.php +++ b/include/classes/user.class.php @@ -163,7 +163,7 @@ public function getTopInviters($limit=10,$start=0) { $invitation->setDebug($this->debug); $invitation->setLog($this->log); $stmt = $this->mysqli->prepare(" - SELECT COUNT(i.account_id) AS invitationcount,a.id,a.username,a.email, + SELECT COUNT(i.account_id) AS invitationcount,a.id,a.username,a.email, (SELECT COUNT(account_id) FROM " . $invitation->getTableName() . " WHERE account_id = i.account_id AND is_activated = 1 GROUP BY account_id) AS activated FROM " . $invitation->getTableName() . " AS i LEFT JOIN " . $this->getTableName() . " AS a @@ -340,38 +340,20 @@ public function getAllAutoPayout() { $this->debug->append("STA " . __METHOD__, 4); $stmt = $this->mysqli->prepare(" SELECT - id, username, coin_address, ap_threshold - FROM " . $this->getTableName() . " - WHERE ap_threshold > 0 - AND coin_address IS NOT NULL + a.id, a.username, ca.coin_address AS coin_address, a.ap_threshold + FROM " . $this->getTableName() . " AS a + LEFT JOIN " . $this->coin_address->getTableName() . " AS ca + ON a.id = ca.account_id + WHERE ap_threshold > 0 AND ca.currency = ? + AND ca.coin_address IS NOT NULL "); - if ( $this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) { + if ( $this->checkStmt($stmt) && $stmt->bind_param('s', $this->config['currency']) && $stmt->execute() && $result = $stmt->get_result()) { return $result->fetch_all(MYSQLI_ASSOC); } $this->debug->append("Unable to fetch users with AP set"); return false; } - /** - * Fetch users coin address - * @param userID int UserID - * @return data string Coin Address - **/ - public function getCoinAddress($userID) { - $this->debug->append("STA " . __METHOD__, 4); - return $this->getSingle($userID, 'coin_address', 'id'); - } - - /** - * Check if a coin address exists already - * @param address string Coin Address - * @return bool True of false - **/ - public function existsCoinAddress($address) { - $this->debug->append("STA " . __METHOD__, 4); - return $this->getSingle($address, 'coin_address', 'coin_address', 's') === $address; - } - /** * Fetch users donation value * @param userID int UserID @@ -519,7 +501,7 @@ public function updateAccount($userID, $address, $threshold, $donate, $email, $t return false; } if (!empty($address)) { - if ($address != $this->getCoinAddress($userID) && $this->existsCoinAddress($address)) { + if ($address != $this->coin_address->getCoinAddress($userID) && $this->coin_address->existsCoinAddress($address)) { $this->setErrorMessage('Address is already in use'); return false; } @@ -559,10 +541,12 @@ public function updateAccount($userID, $address, $threshold, $donate, $email, $t } // We passed all validation checks so update the account - $stmt = $this->mysqli->prepare("UPDATE $this->table SET coin_address = ?, ap_threshold = ?, donate_percent = ?, email = ?, timezone = ?, is_anonymous = ? WHERE id = ?"); - if ($this->checkStmt($stmt) && $stmt->bind_param('sddssii', $address, $threshold, $donate, $email, $timezone, $is_anonymous, $userID) && $stmt->execute()) { + $stmt = $this->mysqli->prepare("UPDATE $this->table SET ap_threshold = ?, donate_percent = ?, email = ?, timezone = ?, is_anonymous = ? WHERE id = ?"); + if ($this->checkStmt($stmt) && $stmt->bind_param('ddssii', $threshold, $donate, $email, $timezone, $is_anonymous, $userID) && $stmt->execute()) { $this->log->log("info", $this->getUserName($userID)." updated their account details"); - return true; + // Update coin address too + if ($this->coin_address->update($userID, $address)) + return true; } // Catchall $this->setErrorMessage('Failed to update your account'); @@ -703,22 +687,18 @@ public function getUserData($userID) { $this->debug->append("Fetching user information for user id: $userID"); $stmt = $this->mysqli->prepare(" SELECT - id, username, pin, api_key, is_admin, is_anonymous, email, timezone, no_fees, - IFNULL(donate_percent, '0') as donate_percent, coin_address, ap_threshold - FROM $this->table + id AS id, username, pin, api_key, is_admin, is_anonymous, email, timezone, no_fees, + IFNULL(donate_percent, '0') as donate_percent, ap_threshold + FROM " . $this->getTableName() . " WHERE id = ? LIMIT 0,1"); - if ($this->checkStmt($stmt)) { - $stmt->bind_param('i', $userID); - if (!$stmt->execute()) { - $this->debug->append('Failed to execute statement'); - return false; - } - $result = $stmt->get_result(); + if ($this->checkStmt($stmt) && $stmt->bind_param('i', $userID) && $stmt->execute() && $result = $stmt->get_result()) { + $aData = $result->fetch_assoc(); + $aData['coin_address'] = $this->coin_address->getCoinAddress($userID); $stmt->close(); - return $result->fetch_assoc(); + return $aData; } $this->debug->append("Failed to fetch user information for $userID"); - return false; + return $this->sqlError(); } /** @@ -742,6 +722,10 @@ public function register($username, $coinaddress, $password1, $password2, $pin, return false; } if (!is_null($coinaddress)) { + if ($this->coin_address->existsCoinAddress($coinaddress)) { + $this->setErrorMessage('Coin address is already taken'); + return false; + } if (!$this->bitcoin->validateaddress($coinaddress)) { $this->setErrorMessage('Coin address is not valid'); return false; @@ -755,7 +739,7 @@ public function register($username, $coinaddress, $password1, $password2, $pin, $this->setErrorMessage( 'This e-mail address is already taken' ); return false; } - if (strlen($password1) < 8) { + if (strlen($password1) < 8) { $this->setErrorMessage( 'Password is too short, minimum of 8 characters required' ); return false; } @@ -801,15 +785,15 @@ public function register($username, $coinaddress, $password1, $password2, $pin, ! $this->setting->getValue('accounts_confirm_email_disabled') ? $is_locked = 1 : $is_locked = 0; $is_admin = 0; $stmt = $this->mysqli->prepare(" - INSERT INTO $this->table (username, pass, email, signup_timestamp, pin, api_key, is_locked, coin_address) - VALUES (?, ?, ?, ?, ?, ?, ?, ?) + INSERT INTO $this->table (username, pass, email, signup_timestamp, pin, api_key, is_locked) + VALUES (?, ?, ?, ?, ?, ?, ?) "); } else { $is_locked = 0; $is_admin = 1; $stmt = $this->mysqli->prepare(" - INSERT INTO $this->table (username, pass, email, signup_timestamp, pin, api_key, is_admin, is_locked, coin_address) - VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?) + INSERT INTO $this->table (username, pass, email, signup_timestamp, pin, api_key, is_admin, is_locked) + VALUES (?, ?, ?, ?, ?, ?, 1, ?) "); } @@ -820,7 +804,9 @@ public function register($username, $coinaddress, $password1, $password2, $pin, $username_clean = strip_tags($username); $signup_time = time(); - if ($this->checkStmt($stmt) && $stmt->bind_param('sssissis', $username_clean, $password_hash, $email1, $signup_time, $pin_hash, $apikey_hash, $is_locked, $coinaddress) && $stmt->execute()) { + if ($this->checkStmt($stmt) && $stmt->bind_param('sssissi', $username_clean, $password_hash, $email1, $signup_time, $pin_hash, $apikey_hash, $is_locked) && $stmt->execute()) { + $new_account_id = $this->mysqli->insert_id; + if (!is_null($coinaddress)) $this->coin_address->add($new_account_id, $coinaddress); if (! $this->setting->getValue('accounts_confirm_email_disabled') && $is_admin != 1) { if ($token = $this->token->createToken('confirm_email', $stmt->insert_id)) { $aData['username'] = $username_clean; @@ -843,7 +829,8 @@ public function register($username, $coinaddress, $password1, $password2, $pin, } else { $this->setErrorMessage( 'Unable to register' ); $this->debug->append('Failed to insert user into DB: ' . $this->mysqli->error); - if ($stmt->sqlstate == '23000') $this->setErrorMessage( 'Username, email or Coinaddress already registered' ); + echo $this->mysqli->error; + if ($stmt->sqlstate == '23000') $this->setErrorMessage( 'Username or email already registered' ); return false; } return false; @@ -997,4 +984,5 @@ public function getCurrentIP($trustremote=false, $checkcloudflare=true, $checkcl $user->setToken($oToken); $user->setBitcoin($bitcoin); $user->setSetting($setting); +$user->setCoinAddress($coin_address); $user->setErrorCodes($aErrorCodes); diff --git a/include/pages/account/edit.inc.php b/include/pages/account/edit.inc.php index 78782ad4e..234f68d6d 100644 --- a/include/pages/account/edit.inc.php +++ b/include/pages/account/edit.inc.php @@ -104,7 +104,7 @@ $_SESSION['POPUP'][] = array('CONTENT' => 'You have not yet unlocked account withdrawls.', 'TYPE' => 'alert alert-danger'); } else if ($aBalance['confirmed'] < $config['mp_threshold']) { $_SESSION['POPUP'][] = array('CONTENT' => 'Payout must be greater or equal than ' . $config['mp_threshold'] . '.', 'TYPE' => 'info'); - } else if (!$user->getCoinAddress($_SESSION['USERDATA']['id'])) { + } else if (!$coin_address->getCoinAddress($_SESSION['USERDATA']['id'])) { $_SESSION['POPUP'][] = array('CONTENT' => 'You have no payout address set.', 'TYPE' => 'alert alert-danger'); } else { $user->log->log("info", $_SESSION['USERDATA']['username']." requesting manual payout"); diff --git a/include/pages/account/workers.inc.php b/include/pages/account/workers.inc.php index eeb9fb267..a672e11c0 100644 --- a/include/pages/account/workers.inc.php +++ b/include/pages/account/workers.inc.php @@ -4,12 +4,10 @@ if ($user->isAuthenticated()) { - if (!$user->getCoinAddress($_SESSION['USERDATA']['id']) AND $setting->getValue('disable_worker_edit')) { - + if (!$coin_address->getCoinAddress($_SESSION['USERDATA']['id']) AND $setting->getValue('disable_worker_edit')) { $_SESSION['POPUP'][] = array('CONTENT' => 'You have no payout address set.', 'TYPE' => 'alert alert-danger'); $_SESSION['POPUP'][] = array('CONTENT' => 'You can not add workers unless a valid Payout Address is set in your User Settings.', 'TYPE' => 'alert alert-danger'); $smarty->assign('CONTENT', 'disabled.tpl'); - } else { switch (@$_REQUEST['do']) { case 'delete': diff --git a/include/version.inc.php b/include/version.inc.php index 91d93ee8d..a4e0d533f 100644 --- a/include/version.inc.php +++ b/include/version.inc.php @@ -2,7 +2,7 @@ $defflip = (!cfip()) ? exit(header('HTTP/1.1 401 Unauthorized')) : 1; define('MPOS_VERSION', '0.0.4'); -define('DB_VERSION', '0.0.11'); +define('DB_VERSION', '0.0.12'); define('CONFIG_VERSION', '0.0.8'); define('HASH_VERSION', 1); diff --git a/scripts/validate_addresses.php b/scripts/validate_addresses.php index 15ad46b27..83c314cc3 100755 --- a/scripts/validate_addresses.php +++ b/scripts/validate_addresses.php @@ -42,6 +42,7 @@ printf($mask, 'Username', 'E-Mail', 'Address', 'Status'); foreach ($users as $aData) { + $aData['coin_address'] = $coin_address->getCoinAddress($aData['id']); if (empty($aData['coin_address']) && $aData['is_locked'] == 0) { $status = 'UNSET'; } else if ($aData['is_locked'] == 1) { diff --git a/scripts/validate_users.php b/scripts/validate_users.php index fe80e2aaf..cd99bbb6b 100755 --- a/scripts/validate_users.php +++ b/scripts/validate_users.php @@ -36,7 +36,6 @@ $username = $user['username']; $loggedIp = $user['loggedIp']; $lastLogin = $user['last_login']; - $coinAddress = $user['coin_address']; $mailAddress = $user['email']; $everLoggedIn = !empty($lastLogin); diff --git a/sql/000_base_structure.sql b/sql/000_base_structure.sql index 85e3e67cf..2299c3b36 100644 --- a/sql/000_base_structure.sql +++ b/sql/000_base_structure.sql @@ -53,6 +53,16 @@ CREATE TABLE IF NOT EXISTS `blocks` ( KEY `time` (`time`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Discovered blocks persisted from Litecoin Service'; +CREATE TABLE IF NOT EXISTS `coin_addresses` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `account_id` int(11) NOT NULL, + `currency` varchar(5) NOT NULL, + `coin_address` varchar(255) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `coin_address` (`coin_address`), + KEY `account_id` (`account_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 + CREATE TABLE IF NOT EXISTS `invitations` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `account_id` int(11) unsigned NOT NULL, diff --git a/upgrade/definitions/0.0.11_to_0.0.12.inc.php b/upgrade/definitions/0.0.11_to_0.0.12.inc.php new file mode 100644 index 000000000..f4ad2b303 --- /dev/null +++ b/upgrade/definitions/0.0.11_to_0.0.12.inc.php @@ -0,0 +1,32 @@ +getValue('DB_VERSION'); // Our actual version installed + + // Upgrade specific variables + $aSql[] = "CREATE TABLE `coin_addresses` ( `id` int(11) NOT NULL AUTO_INCREMENT, `account_id` int(11) NOT NULL, `currency` varchar(55555) NOT NULL, `coin_address` varchar(255) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `coin_address` (`coin_address`), KEY `account_id` (`account_id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8"; + $aSql[] = "INSERT INTO coin_addresses (account_id, currency, coin_address) SELECT id, '" . $config['currency'] . "', coin_address FROM " . $user->getTableName(); + $aSql[] = "ALTER TABLE `" . $user->getTableName() . "` DROP `coin_address`"; + $aSql[] = "UPDATE " . $setting->getTableName() . " SET value = '0.0.12' WHERE name = 'DB_VERSION'"; + + if ($db_version_now == $db_version_old && version_compare($db_version_now, DB_VERSION, '<')) { + // Run the upgrade + echo '- Starting database migration to version ' . $db_version_new . PHP_EOL; + foreach ($aSql as $sql) { + echo '- Preparing: ' . $sql . PHP_EOL; + $stmt = $mysqli->prepare($sql); + if ($stmt && $stmt->execute()) { + echo '- success' . PHP_EOL; + } else { + echo '- failed: ' . $mysqli->error . PHP_EOL; + exit(1); + } + } + } +} +?>