/* * Copyright (c) 2018, evilny0 * * This file is part of cpfm. * * cpfm is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * cpm is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with cpfm. If not, see . * */ #include "wallets/btc.h" WalletHandlerBTC::WalletHandlerBTC(int userId) : WalletHandler(userId) { } WalletHandlerBTC::~WalletHandlerBTC() { } void WalletHandlerBTC::analyzeUserWallets() { linfo << "Analyzing user " << m_userId << " BTC wallets."; mysql::connection db(getMysqlConfig()); const auto wallets = TableWallets{}; const auto wallets_addresses = TableWalletsAddresses{}; map> userWallets; list userAddresses; for (const auto& row: db.run(select(wallets.wallet_id,wallets.type_id,wallets_addresses.address).from(wallets.cross_join(wallets_addresses)).where(wallets.user_id == m_userId and wallets.wallet_id == wallets_addresses.wallet_id and wallets.type_id == CPFM_WALLET_TYPE_ID_BTC))) { userWallets[row.wallet_id].push_back(row.address); userAddresses.push_back(row.address); } for (const auto& w: userWallets) { addUserWalletDataToDB(w.first,w.second,userAddresses); analyzeUserWalletData(w.first); } } void WalletHandlerBTC::analyzeUserWalletData(int walletId) { mysql::connection db(getMysqlConfig()); const auto wallets_tx = TableWalletsTx{}; const auto wallets_balances = TableWalletsBalances{}; // Loop is on blockchain tx id, so first, get blockchain tx list. list blockchainTxs; for (const auto& row: db.run(select(wallets_tx.blockchain_tx_id).from(wallets_tx).where(wallets_tx.wallet_id == walletId).group_by(wallets_tx.blockchain_tx_id).order_by(wallets_tx.timestamp.asc()))) { blockchainTxs.push_back(row.blockchain_tx_id); } ldebug << "------------------------------------------------------------"; Money walletBalance = 0; Money walletInputs = 0; Money walletOutputs = 0; Money walletOutputFees = 0; for (const auto& txId: blockchainTxs) { Money txAmount = 0; Money txFee = 0; for (const auto& row: db.run(select(wallets_tx.amount, wallets_tx.fee).from(wallets_tx).where(wallets_tx.blockchain_tx_id == txId and wallets_tx.wallet_id == walletId))) { Money amount(row.amount); Money fee(row.fee); if (txFee == 0) txFee = fee; txAmount += amount; } txAmount += txFee; if (txAmount<0) walletOutputs += txAmount; else walletInputs += txAmount; walletOutputFees += txFee; walletBalance = walletInputs + walletOutputs - walletOutputFees; string sReason = "?"; if (txAmount < 0) { } /* ldebug << "Tx: " << txId << ". Amount: " << txAmount << ". Outputs: " << walletOutputs << ". Inputs: " << walletInputs << ". Fees: " << txFee << ". Balance: " << walletBalance << ". Reason: " << sReason; */ } linfo << "Wallet " << walletId << " balance is: " << walletBalance; db(insert_into(wallets_balances).set( wallets_balances.wallet_id = walletId, wallets_balances.coin_id = CPFM_COIN_ID_BTC, wallets_balances.balance = walletBalance.toBoostMpf() )); } void WalletHandlerBTC::addUserWalletDataToDB(int walletId, list walletAddresses, list userAddresses) { linfo << "Analyzing wallet " << walletId << "."; for (const auto& a: walletAddresses) { AddressHandlerBTC addr(a,walletId); addr.setWalletAddresses(walletAddresses); addr.setUserAddresses(userAddresses); addr.addAddressDataToDB(); } } AddressHandlerBTC::AddressHandlerBTC(string address, int walletId) { m_address = address; m_walletId = walletId; } AddressHandlerBTC::~AddressHandlerBTC() { } string AddressHandlerBTC::getCacheFilename() { string cacheFilename("data/cache/btc/" + m_address); return cacheFilename; } void AddressHandlerBTC::getAddressData() { linfo << "Getting data for address: " << m_address << "."; if (!bfs::exists(getCacheFilename())) { getBlockchainAddressData(); } } void AddressHandlerBTC::addAddressDataToDB() { linfo << "Analyzing data for address: " << m_address << "."; getAddressData(); addCachedAddressDataToDB(); } void AddressHandlerBTC::getBlockchainAddressData() { try { string sRequestURL = "/fr/rawaddr/"; sRequestURL += m_address; http_client apiclient("https://blockchain.info/"); apiclient.request(methods::GET,sRequestURL).then([](http_response response) { if (response.status_code() == status_codes::OK) { return response.extract_json(); } return pplx::task_from_result(json::value()); }) .then([this](pplx::task previousTask) { ofstream f; f.open(getCacheFilename()); f << previousTask.get(); f.close(); }) .wait(); } catch(const http::http_exception& e) { lerr << "Failed to query blockchain.info about " << m_address; } } void AddressHandlerBTC::addCachedAddressDataToDB() { ifstream f; f.open(getCacheFilename()); json::value jvalue = json::value::parse(f); f.close(); addAddressDataJSONToDB(jvalue); } void AddressHandlerBTC::addAddressDataJSONToDB(json::value jvalue) { mysql::connection db(getMysqlConfig()); const auto wallets_tx = TableWalletsTx{}; string myAddr = jvalue["address"].as_string(); linfo << "Analyzing address: " << myAddr; Money balance = jvalue["final_balance"].as_integer();; for (int i=0;i inputAddrList; list outputAddrList; for (int j=0;j