/*
* 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