You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

353 lines
10 KiB

/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "wallets/eth.h"
WalletHandlerETH::WalletHandlerETH(int userId) : WalletHandler(userId)
{
}
WalletHandlerETH::~WalletHandlerETH()
{
}
void WalletHandlerETH::analyzeUserWallets()
{
linfo << "Analyzing user " << m_userId << " ETH wallets.";
mysql::connection db(getMysqlConfig());
const auto wallets = TableWallets{};
const auto wallets_addresses = TableWalletsAddresses{};
map<int,list<string>> userWallets;
list<string> 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_ETH)))
{
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 WalletHandlerETH::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<string> 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_ETH,
wallets_balances.balance = walletBalance.toBoostMpf()
));
}
void WalletHandlerETH::addUserWalletDataToDB(int walletId, list<string> walletAddresses, list<string> userAddresses)
{
linfo << "Analyzing wallet " << walletId << ".";
for (const auto& a: walletAddresses)
{
AddressHandlerETH addr(a,walletId);
addr.setWalletAddresses(walletAddresses);
addr.setUserAddresses(userAddresses);
addr.addAddressDataToDB();
}
}
AddressHandlerETH::AddressHandlerETH(string address, int walletId)
{
m_address = address;
m_walletId = walletId;
}
AddressHandlerETH::~AddressHandlerETH()
{
}
string AddressHandlerETH::getCacheFilename()
{
string cacheFilename("data/cache/eth/" + m_address);
return cacheFilename;
}
void AddressHandlerETH::getAddressData()
{
linfo << "Getting data for address: " << m_address << ".";
if (!bfs::exists(getCacheFilename()))
{
getBlockchainAddressData();
}
}
void AddressHandlerETH::addAddressDataToDB()
{
linfo << "Analyzing data for address: " << m_address << ".";
getAddressData();
addCachedAddressDataToDB();
}
void AddressHandlerETH::getBlockchainAddressData()
{
try
{
string sRequestURL = "/api?module=account&action=balance&address=";
sRequestURL += m_address;
http_client apiclient("https://api.etherscan.io");
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<json::value> previousTask)
{
ofstream f;
f.open(getCacheFilename());
f << previousTask.get();
f.close();
})
.wait();
}
catch(const http::http_exception& e)
{
lerr << "Failed to query cashexplorer.bitcoin.com about " << m_address;
}
}
void AddressHandlerETH::addCachedAddressDataToDB()
{
ifstream f;
f.open(getCacheFilename());
json::value jvalue = json::value::parse(f);
f.close();
addAddressDataJSONToDB(jvalue);
}
void AddressHandlerETH::addAddressDataJSONToDB(json::value jvalue)
{
mysql::connection db(getMysqlConfig());
const auto wallets_tx = TableWalletsTx{};
string myAddr = m_address;
linfo << "Analyzing address: " << myAddr;
if (!jvalue["message"].as_string().compare("OK") && !jvalue["status"].as_string().compare("1"))
{
Money walletBalance (jvalue["result"]);
Money divider("1000000000000000000");
walletBalance /= divider;
linfo << "Wallet " << m_walletId << " balance is: " << walletBalance;
mysql::connection db(getMysqlConfig());
const auto wallets_tx = TableWalletsTx{};
const auto wallets_balances = TableWalletsBalances{};
db(insert_into(wallets_balances).set(
wallets_balances.wallet_id = m_walletId,
wallets_balances.coin_id = CPFM_COIN_ID_ETH,
wallets_balances.balance = walletBalance.toBoostMpf()
));
}
/*
//Money balance = jvalue["final_balance"].as_integer();;
for (int i=0;i<jvalue["txs"].size();i++)
{
string txid = jvalue["txs"][i]["txid"].as_string();
int timestamp = jvalue["txs"][i]["time"].as_integer();
Money txTotalOutputAmount;
Money txTotalInputAmount;
Money txMyOutputAmount;
Money txMyInputAmount;
bool bToMyAddr = false;
bool bFromMyAddr = false;
list<string> inputAddrList;
list<string> outputAddrList;
for (int j=0;j<jvalue["txs"][i]["vin"].size();j++)
{
string inputAddr = jvalue["txs"][i]["vin"][j]["addr"].as_string();
Money amount (jvalue["txs"][i]["vin"][j]["value"]);
txTotalInputAmount += amount;
if (inputAddr == myAddr)
{
bFromMyAddr = true;
txMyInputAmount += amount;
}
else
{
inputAddrList.push_back(inputAddr);
}
}
for (int j=0;j<jvalue["txs"][i]["vout"].size();j++)
{
string outputAddr = jvalue["txs"][i]["vout"][j]["scriptPubKey"]["addresses"][0].as_string();
Money amount(jvalue["txs"][i]["vout"][j]["value"]);
txTotalOutputAmount += amount;
if (outputAddr == myAddr)
{
bToMyAddr = true;
txMyOutputAmount += amount;
}
else
{
outputAddrList.push_back(outputAddr);
}
}
Money amount;
Money fee;
if (bFromMyAddr)
{
amount = -txMyInputAmount;
fee = txTotalInputAmount - txTotalOutputAmount;
for (auto const& addr: inputAddrList)
{
bool bFoundInUserAddresses = false;
for (auto const& userAddr: m_userAddresses)
{
if (!userAddr.compare(addr))
{
bFoundInUserAddresses = true;
break;
}
}
if (!bFoundInUserAddresses)
{
lwarn << "User configuration is missing this address: " << addr;
}
}
for (auto const& addr: outputAddrList)
{
bool bFoundInUserAddresses = false;
for (auto const& userAddr: m_userAddresses)
{
if (!userAddr.compare(addr))
{
bFoundInUserAddresses = true;
break;
}
}
if (!bFoundInUserAddresses)
{
linfo << "Potential other address to analyze: " << L_Cyan << addr;
}
}
}
if (bToMyAddr)
{
amount = txMyOutputAmount;
fee = 0;
}
db(insert_into(wallets_tx).set(
wallets_tx.wallet_id = m_walletId,
wallets_tx.tx_hash = txid,
wallets_tx.amount = amount.toBoostMpf(),
wallets_tx.fee = fee.toBoostMpf(),
wallets_tx.timestamp = timestamp
));
}
*/
}