Compare commits

..

No commits in common. "a41b13f3b2639674a09b5944f459ea2836408b2d" and "b0bd473e961f6cbcde9d98819087525bbfa0821d" have entirely different histories.

40 changed files with 1318 additions and 2912 deletions

View File

@ -38,7 +38,6 @@ set(LIBS ${LIBS} "-lcpprest")
include_directories(src)
include_directories(src/wallets)
include_directories(src/exchanges)
include_directories(src/datasources)
set(CMAKE_CXX_STANDARD 11)

View File

@ -6,7 +6,7 @@ Cryptocurrencies portfolio manager.
* MySQL
* C++ REST SDK : https://github.com/Microsoft/cpprestsdk
* OpenSSL (1.1.0+)
* OpenSSL
* GMP
* Boost
* sqlpp11 (modified to handle decimal type) : https://github.com/evilny0/sqlpp11/tree/decimal

View File

@ -29,7 +29,7 @@ if (SQLPP11_MYSQL_MAIN_HEADER)
)
if("${check_result}" STREQUAL "")
string(APPEND SQLPP11_MYSQL_NOT_FOUND_MESSAGE "\nRejecting found '${SQLPP11_MYSQL_MAIN_HEADER}', it seems to be invalid.")
string(APPEND SQLPP11_NOT_FOUND_MESSAGE "\nRejecting found '${SQLPP11_MYSQL_MAIN_HEADER}', it seems to be invalid.")
unset(SQLPP11_MYSQL_INCLUDE_DIR CACHE)
else()
# Check succeeded, create target

View File

@ -7,37 +7,6 @@ SET time_zone = "+00:00";
/*!40101 SET NAMES utf8mb4 */;
CREATE TABLE `blockchain_btc_raw_tx` (
`raw_tx_id` int(11) NOT NULL,
`blockchain_id` int(11) NOT NULL,
`hash` varchar(200) NOT NULL,
`unix_time` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `blockchain_btc_raw_tx_details` (
`raw_tx_detail_id` int(11) NOT NULL,
`raw_tx_id` int(11) NOT NULL,
`amount` decimal(40,18) NOT NULL,
`address` varchar(100) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `blockchain_eth_raw_tx` (
`raw_tx_id` int(11) NOT NULL,
`blockchain_id` int(11) NOT NULL,
`hash` varchar(200) NOT NULL,
`unix_time` int(11) NOT NULL,
`fee` decimal(40,18) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `blockchain_eth_raw_tx_details` (
`raw_tx_detail_id` int(11) NOT NULL,
`raw_tx_id` int(11) NOT NULL,
`address_from` varchar(200) NOT NULL,
`address_to` varchar(200) NOT NULL,
`amount` decimal(40,18) NOT NULL,
`amount_coin_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `coins` (
`coin_id` int(11) NOT NULL,
`coin_name` varchar(50) NOT NULL,
@ -63,7 +32,7 @@ CREATE TABLE `exchanges_ledgers` (
`account_id` int(11) NOT NULL,
`exchange_ledger_id` varchar(50) NOT NULL,
`exchange_reference_id` varchar(50) NOT NULL,
`unix_time` int(11) NOT NULL,
`timestamp` int(11) NOT NULL,
`operation_type` int(11) NOT NULL,
`coin_id` int(11) NOT NULL,
`amount` decimal(40,18) NOT NULL,
@ -77,7 +46,7 @@ CREATE TABLE `exchanges_trades` (
`exchange_order_id` varchar(50) NOT NULL,
`base_coin_id` int(11) NOT NULL,
`quote_coin_id` int(11) NOT NULL,
`unix_time` int(11) NOT NULL,
`timestamp` int(11) NOT NULL,
`type` int(11) NOT NULL,
`order_type` int(11) NOT NULL,
`price` decimal(40,18) NOT NULL,
@ -91,8 +60,7 @@ CREATE TABLE `wallets` (
`wallet_id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
`type_id` int(11) NOT NULL,
`wallet_name` varchar(50) NOT NULL,
`active` tinyint(1) NOT NULL
`wallet_name` varchar(50) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `wallets_addresses` (
@ -109,40 +77,16 @@ CREATE TABLE `wallets_balances` (
`balance` decimal(48,18) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `wallets_btc_raw_tx` (
`raw_tx_id` int(11) NOT NULL,
`wallet_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `wallets_eth_raw_tx` (
`raw_tx_id` int(11) NOT NULL,
`wallet_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `wallets_tx` (
`tx_id` int(11) NOT NULL,
`blockchain_tx_id` varchar(200) NOT NULL,
`wallet_id` int(11) NOT NULL,
`amount` decimal(40,18) NOT NULL,
`fee` decimal(40,18) NOT NULL,
`amount_coin_id` int(11) NOT NULL,
`fee_coin_id` int(11) NOT NULL,
`unix_time` int(11) NOT NULL,
`operation_type` int(11) NOT NULL
`timestamp` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
ALTER TABLE `blockchain_btc_raw_tx`
ADD PRIMARY KEY (`raw_tx_id`);
ALTER TABLE `blockchain_btc_raw_tx_details`
ADD PRIMARY KEY (`raw_tx_detail_id`);
ALTER TABLE `blockchain_eth_raw_tx`
ADD PRIMARY KEY (`raw_tx_id`);
ALTER TABLE `blockchain_eth_raw_tx_details`
ADD PRIMARY KEY (`raw_tx_detail_id`);
ALTER TABLE `exchanges_accounts`
ADD PRIMARY KEY (`account_id`);
@ -159,20 +103,12 @@ ALTER TABLE `wallets_addresses`
ADD PRIMARY KEY (`address_id`);
ALTER TABLE `wallets_balances`
ADD PRIMARY KEY (`wallet_id`,`coin_id`);
ADD PRIMARY KEY (`wallet_id`);
ALTER TABLE `wallets_tx`
ADD PRIMARY KEY (`tx_id`);
ALTER TABLE `blockchain_btc_raw_tx`
MODIFY `raw_tx_id` int(11) NOT NULL AUTO_INCREMENT;
ALTER TABLE `blockchain_btc_raw_tx_details`
MODIFY `raw_tx_detail_id` int(11) NOT NULL AUTO_INCREMENT;
ALTER TABLE `blockchain_eth_raw_tx`
MODIFY `raw_tx_id` int(11) NOT NULL AUTO_INCREMENT;
ALTER TABLE `blockchain_eth_raw_tx_details`
MODIFY `raw_tx_detail_id` int(11) NOT NULL AUTO_INCREMENT;
ALTER TABLE `exchanges_accounts`
MODIFY `account_id` int(11) NOT NULL AUTO_INCREMENT;
ALTER TABLE `exchanges_ledgers`
@ -187,4 +123,4 @@ ALTER TABLE `wallets_tx`
MODIFY `tx_id` int(11) NOT NULL AUTO_INCREMENT;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

View File

@ -1,7 +1,6 @@
aux_source_directory(. SOURCE_LIST)
aux_source_directory(wallets SOURCE_LIST_WALLETS)
aux_source_directory(exchanges SOURCE_LIST_EXCHANGES)
aux_source_directory(datasources SOURCE_LIST_DATASOURCES)
add_executable(${EXECUTABLE} ${SOURCE_LIST} ${SOURCE_LIST_WALLETS} ${SOURCE_LIST_EXCHANGES} ${SOURCE_LIST_DATASOURCES})
add_executable(${EXECUTABLE} ${SOURCE_LIST} ${SOURCE_LIST_WALLETS} ${SOURCE_LIST_EXCHANGES})
target_link_libraries(${EXECUTABLE} ${LIBS})

View File

@ -1,137 +0,0 @@
/*
* Copyright (c) 2021, 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 "datasources/blockchain_info.h"
list<BlockchainTxDetailsTypeBTC> BlockchainDataSourceBTC_BlockchainInfo::getTxDetailsListForAddress(string address)
{
linfo << "Blockchain.info : Analyzing address: " << address;
list<BlockchainTxDetailsTypeBTC> l;
if (!bfs::exists(getCacheFilenameForAddress(address)))
{
saveBlockchainAddressDataToCacheFile(address);
}
if (!bfs::exists(getCacheFilenameForAddress(address)))
{
lerr << "Blockchain.info : cache file could not be found. Address data was not retrieved.";
return l;
}
ifstream f;
f.open(getCacheFilenameForAddress(address));
json::value jvalue = json::value::parse(f);
f.close();
linfo << "Blockchain.info : analyzing address: " << jvalue["address"].as_string();
for (int i=0;i<jvalue["txs"].size();i++)
{
BlockchainTxDetailsTypeBTC tx;
tx.hash = jvalue["txs"][i]["hash"].as_string();
tx.time.setFromUnixTime(jvalue["txs"][i]["time"].as_integer());
for (int j=0;j<jvalue["txs"][i]["inputs"].size();j++)
{
string addr = jvalue["txs"][i]["inputs"][j]["prev_out"]["addr"].as_string();
__int64 amount = jvalue["txs"][i]["inputs"][j]["prev_out"]["value"].as_integer();
Money m = amount;
Money mdecimals = m/100000000;
// The same address can be multiple time in the inputs, so we need to sum the amounts.
tx.inputs[addr] += mdecimals;
}
for (int j=0;j<jvalue["txs"][i]["out"].size();j++)
{
string addr = jvalue["txs"][i]["out"][j]["addr"].as_string();
__int64 amount = jvalue["txs"][i]["out"][j]["value"].as_integer();
Money m = amount;
Money mdecimals = m/100000000;
// The same address can only be once in the outputs.
tx.outputs[addr] = mdecimals;
}
l.push_back(tx);
}
return l;
}
string BlockchainDataSourceBTC_BlockchainInfo::getCacheFilenameForAddress(string address)
{
string cacheFilename("data/cache/blockchain.info/btc/" + address);
return cacheFilename;
}
void BlockchainDataSourceBTC_BlockchainInfo::saveBlockchainAddressDataToCacheFile(string address)
{
try
{
linfo << "Blockchain.info : querying API about " << address;
string sRequestURL = "/rawaddr/";
sRequestURL += address;
http_client apiclient("https://blockchain.info/");
m_currentRequestCacheFilename = getCacheFilenameForAddress(address);
apiclient.request(methods::GET,sRequestURL).then([](http_response response)
{
if (response.status_code() == status_codes::OK)
{
ldebug << "Blockchain.info : response OK.";
return response.extract_json();
}
else if (response.status_code() == status_codes::TooManyRequests)
{
lwarn << "Blockchain.info : too many queries! We are being rate limited.";
pplx::task_from_result(json::value());
}
return pplx::task_from_result(json::value());
})
.then([this](pplx::task<json::value> previousTask)
{
if (previousTask.get() != json::value::null())
{
linfo << "Blockchain.info : saving query result to " << m_currentRequestCacheFilename;
ofstream f;
f.open(m_currentRequestCacheFilename);
f << previousTask.get();
f.close();
}
else
{
lerr << "Blockchain.info : query result is empty. Nothing will be saved to the cache file.";
}
})
.wait();
}
catch(const http::http_exception& e)
{
lerr << "Blockchain.info : failed to query API about " << address;
}
}

View File

@ -1,39 +0,0 @@
/*
* Copyright (c) 2021, 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/>.
*
*/
#ifndef CPFM_DATASOURCE_BLOCKCHAIN_INFO_H_INCLUDED
#define CPFM_DATASOURCE_BLOCKCHAIN_INFO_H_INCLUDED
#include "datasources/datasource.h"
class BlockchainDataSourceBTC_BlockchainInfo
{
public:
list<BlockchainTxDetailsTypeBTC> getTxDetailsListForAddress(string address);
private:
string getCacheFilenameForAddress(string address);
void saveBlockchainAddressDataToCacheFile(string address);
string m_currentRequestCacheFilename;
};
#endif // CPFM_DATASOURCE_BLOCKCHAIN_INFO_H_INCLUDED

View File

@ -1,245 +0,0 @@
/*
* Copyright (c) 2021, 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 "datasources/blockchair.h"
Time BlockchainDataSourceTypeBTC_Blockchair::getTimeFromString(string s)
{
std::istringstream in{s};
date::sys_time<std::chrono::seconds> tp;
in >> date::parse("%F %T", tp);
Time t;
t.setFromUnixTime(std::chrono::system_clock::to_time_t(tp));
return t;
}
list<BlockchainTxDetailsTypeBTC> BlockchainDataSourceTypeBTC_Blockchair::getTxDetailsListForAddresses(list<string> addresses)
{
linfo << "Blockchair : analyzing address list";
list<BlockchainTxDetailsTypeBTC> l;
if (!bfs::exists(getCacheFilenameForAddresses(addresses)))
{
saveBlockchainAddressesDataToCacheFile(addresses);
}
if (!bfs::exists(getCacheFilenameForAddresses(addresses)))
{
lerr << "Blockchair : cache file could not be found. Addresses data was not retrieved.";
return l;
}
ifstream f;
f.open(getCacheFilenameForAddresses(addresses));
json::value jvalue = json::value::parse(f);
f.close();
for (int i=0;i<jvalue["data"]["transactions"].size();i++)
{
string txHash = jvalue["data"]["transactions"][i].as_string();
linfo << "Blockchair : found tx " << txHash << ". Starting analysis.";
if (!bfs::exists(getCacheFilenameForTx(txHash)))
{
saveBlockchainTxDataToCacheFile(txHash);
}
if (!bfs::exists(getCacheFilenameForTx(txHash)))
{
lerr << "Blockchair : cache file for tx " << txHash << " could not be found. Tx data was not retrieved.";
}
ifstream fTx;
fTx.open(getCacheFilenameForTx(txHash));
json::value jvalueTx = json::value::parse(fTx);
fTx.close();
BlockchainTxDetailsTypeBTC tx;
tx.hash = txHash;
tx.time = getTimeFromString (jvalueTx["data"][txHash]["transaction"]["time"].as_string());
for (int j=0;j<jvalueTx["data"][txHash]["inputs"].size();j++)
{
string addr = jvalueTx["data"][txHash]["inputs"][j]["recipient"].as_string();
__int64 amount = jvalueTx["data"][txHash]["inputs"][j]["value"].as_integer();
Money m = amount;
Money mdecimals = m/100000000;
// The same address can be multiple time in the inputs, so we need to sum the amounts.
tx.inputs[addr] += mdecimals;
}
for (int j=0;j<jvalueTx["data"][txHash]["outputs"].size();j++)
{
string addr = jvalueTx["data"][txHash]["outputs"][j]["recipient"].as_string();
__int64 amount = jvalueTx["data"][txHash]["outputs"][j]["value"].as_integer();
Money m = amount;
Money mdecimals = m/100000000;
// The same address can only be once in the outputs.
tx.outputs[addr] = mdecimals;
}
l.push_back(tx);
linfo << "Blockchair : finished analysis for tx " << txHash << ".";
}
linfo << "Blockchair : finished analysis for address list.";
return l;
}
string BlockchainDataSourceTypeBTC_Blockchair::getCacheFilenameForAddresses(list<string> addresses)
{
string s;
for (const auto a: addresses)
s = s+a;
string cacheFilename("data/cache/blockchair/"+m_blockchainName+"/addresses/" + s);
return cacheFilename;
}
string BlockchainDataSourceTypeBTC_Blockchair::getCacheFilenameForTx(string txHash)
{
string cacheFilename("data/cache/blockchair/"+m_blockchainName+"/tx/" + txHash);
return cacheFilename;
}
void BlockchainDataSourceTypeBTC_Blockchair::saveBlockchainAddressesDataToCacheFile(list<string> addresses)
{
try
{
string s;
for (const auto a: addresses)
{
if (s.length())
s = s+","+a;
else
s = a;
}
linfo << "Blockchair : querying API about addresses : " << s;
string sRequestURL = "/"+m_blockchainName+"/dashboards/addresses/";
sRequestURL += s;
http_client apiclient("https://api.blockchair.com/");
m_currentRequestCacheFilename = getCacheFilenameForAddresses(addresses);
ltrace << "Blockchair : query : " << sRequestURL;
apiclient.request(methods::GET,sRequestURL).then([](http_response response)
{
if (response.status_code() == status_codes::OK)
{
ldebug << "Blockchair : response OK.";
return response.extract_json();
}
else if (response.status_code() == status_codes::TooManyRequests)
{
lwarn << "Blockchair : too many queries! We are being rate limited.";
pplx::task_from_result(json::value());
}
return pplx::task_from_result(json::value());
})
.then([this](pplx::task<json::value> previousTask)
{
if (previousTask.get() != json::value::null())
{
linfo << "Blockchair : saving query result to " << m_currentRequestCacheFilename;
ofstream f;
f.open(m_currentRequestCacheFilename);
f << previousTask.get();
f.close();
}
else
{
lerr << "Blockchair : query result is empty. Nothing will be saved to the cache file.";
}
})
.wait();
}
catch(const http::http_exception& e)
{
lerr << "Blockchair : failed to query API";
}
}
void BlockchainDataSourceTypeBTC_Blockchair::saveBlockchainTxDataToCacheFile(string txHash)
{
try
{
linfo << "Blockchair : querying API about tx : " << txHash;
string sRequestURL = "/"+m_blockchainName+"/dashboards/transaction/";
sRequestURL += txHash;
http_client apiclient("https://api.blockchair.com/");
m_currentRequestCacheFilename = getCacheFilenameForTx(txHash);
ltrace << "Blockchair : query : " << sRequestURL;
apiclient.request(methods::GET,sRequestURL).then([](http_response response)
{
if (response.status_code() == status_codes::OK)
{
ldebug << "Blockchair : response OK.";
return response.extract_json();
}
else if (response.status_code() == status_codes::TooManyRequests)
{
lwarn << "Blockchair : too many queries! We are being rate limited.";
pplx::task_from_result(json::value());
}
return pplx::task_from_result(json::value());
})
.then([this](pplx::task<json::value> previousTask)
{
if (previousTask.get() != json::value::null())
{
linfo << "Blockchair : saving query result to " << m_currentRequestCacheFilename;
ofstream f;
f.open(m_currentRequestCacheFilename);
f << previousTask.get();
f.close();
}
else
{
lerr << "Blockchair : query result is empty. Nothing will be saved to the cache file.";
}
})
.wait();
}
catch(const http::http_exception& e)
{
lerr << "Blockchair : failed to query API about " << txHash;
}
}

View File

@ -1,67 +0,0 @@
/*
* Copyright (c) 2021, 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/>.
*
*/
#ifndef CPFM_DATASOURCE_BLOCKCHAIR_H_INCLUDED
#define CPFM_DATASOURCE_BLOCKCHAIR_H_INCLUDED
#include "datasources/datasource.h"
class BlockchainDataSourceTypeBTC_Blockchair
{
public:
list<BlockchainTxDetailsTypeBTC> getTxDetailsListForAddresses(list<string> addresses);
private:
string getCacheFilenameForAddresses(list<string> address);
string getCacheFilenameForTx(string txHash);
void saveBlockchainAddressesDataToCacheFile(list<string> address);
void saveBlockchainTxDataToCacheFile(string txHash);
Time getTimeFromString(string s);
string m_currentRequestCacheFilename;
protected:
string m_blockchainName;
virtual void dummyMakeAbstract() = 0;
};
class BlockchainDataSourceBTC_Blockchair : public BlockchainDataSourceTypeBTC_Blockchair
{
public:
BlockchainDataSourceBTC_Blockchair() { m_blockchainName = "bitcoin"; }
protected:
virtual void dummyMakeAbstract(){};
};
class BlockchainDataSourceBCH_Blockchair : public BlockchainDataSourceTypeBTC_Blockchair
{
public:
BlockchainDataSourceBCH_Blockchair() { m_blockchainName = "bitcoin-cash"; }
protected:
virtual void dummyMakeAbstract(){};
};
#endif // CPFM_DATASOURCE_BLOCKCHAIR_H_INCLUDED

View File

@ -1,77 +0,0 @@
/*
* Copyright (c) 2021, 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 "datasources/datasource.h"
#include "datasources/blockchain_info.h"
#include "datasources/blockchair.h"
#include "datasources/etherscan.h"
list<BlockchainTxDetailsTypeBTC> BlockchainDataSourceBTC::getTxDetailsListForAddresses(list<string> addresses)
{
list<BlockchainTxDetailsTypeBTC> l;
// Blockchain.info
/*
BlockchainDataSourceBTC_BlockchainInfo bci;
list<string> hashList;
for (auto const address: addresses)
{
list<BlockchainTxDetailsTypeBTC> listForSingleAddress = bci.getTxDetailsListForAddress(address);
for (auto const tx: listForSingleAddress)
{
if (std::find(hashList.begin(), hashList.end(), tx.hash) == hashList.end())
{
hashList.push_back(tx.hash);
l.push_back(tx);
}
}
}
*/
// Blockchair
BlockchainDataSourceBTC_Blockchair bc;
l = bc.getTxDetailsListForAddresses(addresses);
return l;
}
list<BlockchainTxDetailsTypeBTC> BlockchainDataSourceBCH::getTxDetailsListForAddresses(list<string> addresses)
{
list<BlockchainTxDetailsTypeBTC> l;
// Blockchair
BlockchainDataSourceBCH_Blockchair bc;
l = bc.getTxDetailsListForAddresses(addresses);
return l;
}
list<BlockchainTxDetailsTypeETH> BlockchainDataSourceETH::getTxDetailsListForAddress(string address)
{
list<BlockchainTxDetailsTypeETH> l;
// Etherscan
BlockchainDataSourceETH_Etherscan bc;
l = bc.getTxDetailsListForAddress(address);
return l;
}

View File

@ -1,71 +0,0 @@
/*
* Copyright (c) 2021, 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/>.
*
*/
#ifndef CPFM_DATASOURCE_H_INCLUDED
#define CPFM_DATASOURCE_H_INCLUDED
#include "pf.h"
class BlockchainTxDetailsTypeBTC
{
public:
string hash;
Time time;
map <string,Money> inputs;
map <string,Money> outputs;
};
class BlockchainTxOperationTypeETH
{
public:
string addressFrom;
string addressTo;
Money amount;
int amountCoinId;
};
class BlockchainTxDetailsTypeETH
{
public:
string hash;
Time time;
list<BlockchainTxOperationTypeETH> operations;
Money fee;
};
class BlockchainDataSourceBTC
{
public:
list<BlockchainTxDetailsTypeBTC> getTxDetailsListForAddresses(list<string> addresses);
};
class BlockchainDataSourceBCH
{
public:
list<BlockchainTxDetailsTypeBTC> getTxDetailsListForAddresses(list<string> addresses);
};
class BlockchainDataSourceETH
{
public:
list<BlockchainTxDetailsTypeETH> getTxDetailsListForAddress(string address);
};
#endif // CPFM_DATASOURCE_H_INCLUDED

View File

@ -1,323 +0,0 @@
/*
* Copyright (c) 2021, 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 "datasources/etherscan.h"
#define TXLIST_NORMAL "txlist"
#define TXLIST_INTERNAL "txlistinternal"
#define TXLIST_TOKENS "tokentx"
list<BlockchainTxDetailsTypeETH> BlockchainDataSourceETH_Etherscan::getTxDetailsListForAddress(string address)
{
linfo << "Etherscan : Analyzing address: " << address;
if (!retrieveCacheFilesForAddress(address))
{
list<BlockchainTxDetailsTypeETH> l;
lerr << "Etherscan : cache files could not be found. Address data was not retrieved.";
return l;
}
list<string> hashListNormal;
list<string> hashListInternal;
list<string> hashListToken;
list<BlockchainTxDetailsTypeETH> lNormal = getTxDetailsListForAddressTxType(address,TXLIST_NORMAL,hashListNormal);
list<BlockchainTxDetailsTypeETH> lInternal = getTxDetailsListForAddressTxType(address,TXLIST_INTERNAL,hashListInternal);
list<BlockchainTxDetailsTypeETH> lToken = getTxDetailsListForAddressTxType(address,TXLIST_TOKENS,hashListToken);
list<BlockchainTxDetailsTypeETH> l = lNormal;
// Add internals tx to the list
for (auto const tx: lInternal)
{
BlockchainTxDetailsTypeETH txNew = tx;
list<BlockchainTxDetailsTypeETH>::iterator it;
for (it=l.begin();it!=l.end();it++)
{
if (it->hash == tx.hash)
{
txNew = (*it);
for (const auto op: tx.operations)
{
txNew.operations.push_back(op);
}
l.erase(it);
break;
}
}
l.push_back(txNew);
}
// Add tokens tx to the list
for (auto const tx: lToken)
{
BlockchainTxDetailsTypeETH txNew = tx;
list<BlockchainTxDetailsTypeETH>::iterator it;
for (it=l.begin();it!=l.end();it++)
{
if (it->hash == tx.hash)
{
txNew = (*it);
for (const auto op: tx.operations)
{
txNew.operations.push_back(op);
}
l.erase(it);
break;
}
}
l.push_back(txNew);
}
return l;
}
list<BlockchainTxDetailsTypeETH> BlockchainDataSourceETH_Etherscan::getTxDetailsListForAddressTxType(string address, string type, list<string>& hashList)
{
list<BlockchainTxDetailsTypeETH> l;
ifstream f;
f.open(getCacheFilenameForAddress(address,type));
json::value jvalue = json::value::parse(f);
f.close();
linfo << "Etherscan : analyzing " << type << " for address: " << address;
if (!jvalue["status"].as_string().compare("1"))
{
for (int i=0;i<jvalue["result"].size();i++)
{
ltrace << "Found tx " << jvalue["result"][i]["hash"].as_string();
BlockchainTxDetailsTypeETH tx;
// Search if the hash is already in the list. If yes, remove the element.
// It will be added after the operation list is updated.
list<BlockchainTxDetailsTypeETH>::iterator it;
for (it=l.begin();it!=l.end();it++)
{
if (it->hash == jvalue["result"][i]["hash"].as_string())
{
ltrace << "Tx already existed. Removing from list so it can be updated with new values.";
tx = (*it);
l.erase(it);
break;
}
}
tx.hash = jvalue["result"][i]["hash"].as_string();
tx.time.setFromUnixTime(strtol(jvalue["result"][i]["timeStamp"].as_string().c_str(),NULL,10));
Money gasUsed (jvalue["result"][i]["gasUsed"]);
Money gasPrice (jvalue["result"][i]["gasPrice"]);
Money fee = gasPrice*gasUsed;
Money feeDivider("1000000000000000000");
fee /= feeDivider;
tx.fee = fee;
if (type == TXLIST_NORMAL)
{
BlockchainTxOperationTypeETH op;
op.addressFrom = jvalue["result"][i]["from"].as_string();
op.addressTo = jvalue["result"][i]["to"].as_string();
op.amountCoinId = CPFM_COIN_ID_ETH;
Money divider("1000000000000000000");
Money m(jvalue["result"][i]["value"]);
m /= divider;
op.amount = m;
ltrace << "Operation. Amount: " << op.amount << ". Coin : " << getCoinName(op.amountCoinId);
tx.operations.push_back(op);
}
else if (type == TXLIST_TOKENS)
{
BlockchainTxOperationTypeETH op;
op.addressFrom = jvalue["result"][i]["from"].as_string();
op.addressTo = jvalue["result"][i]["to"].as_string();
op.amountCoinId = GetCoinIdFromContractAddress(jvalue["result"][i]["contractAddress"].as_string());
if (!op.amountCoinId)
{
lerr << "Unknown token : " << jvalue["result"][i]["tokenSymbol"].as_string() << ". Tx will be ignored.";
continue;
}
Money divider = 1;
int multiplier = strtol(jvalue["result"][i]["tokenDecimal"].as_string().c_str(),NULL,10);
for (int i=0;i<multiplier;i++)
{
divider*=10;
}
Money m(jvalue["result"][i]["value"]);
m /= divider;
op.amount = m;
ltrace << "Operation. Amount: " << op.amount << ". Coin : " << getCoinName(op.amountCoinId);
tx.operations.push_back(op);
}
else if (type == TXLIST_TOKENS)
{
lerr << "Internal tx are not supported.";
continue;
}
else
{
lerr << "Unknown tx type : " << type << ". Tx will be ignored.";
continue;
}
hashList.push_back(tx.hash);
l.push_back(tx);
}
}
return l;
}
bool BlockchainDataSourceETH_Etherscan::retrieveCacheFilesForAddress(string address)
{
if (!bfs::exists(getCacheFilenameForAddress(address,TXLIST_NORMAL)))
{
saveBlockchainAddressDataToCacheFile(address,TXLIST_NORMAL);
}
if (!bfs::exists(getCacheFilenameForAddress(address,TXLIST_NORMAL)))
{
lerr << "Etherscan : cache file for " << TXLIST_NORMAL << " could not be found.";
return false;
}
if (!bfs::exists(getCacheFilenameForAddress(address,TXLIST_INTERNAL)))
{
saveBlockchainAddressDataToCacheFile(address,TXLIST_INTERNAL);
}
if (!bfs::exists(getCacheFilenameForAddress(address,TXLIST_INTERNAL)))
{
lerr << "Etherscan : cache file for " << TXLIST_INTERNAL << " could not be found.";
return false;
}
if (!bfs::exists(getCacheFilenameForAddress(address,TXLIST_TOKENS)))
{
saveBlockchainAddressDataToCacheFile(address,TXLIST_TOKENS);
}
if (!bfs::exists(getCacheFilenameForAddress(address,TXLIST_TOKENS)))
{
lerr << "Etherscan : cache file for " << TXLIST_TOKENS << " could not be found.";
return false;
}
return true;
}
string BlockchainDataSourceETH_Etherscan::getCacheFilenameForAddress(string address, string type)
{
string cacheFilename("data/cache/etherscan.io/" + type + "/" + address);
return cacheFilename;
}
void BlockchainDataSourceETH_Etherscan::saveBlockchainAddressDataToCacheFile(string address, string type)
{
try
{
sleep (5);
linfo << "Etherscan : querying API about " << type << " for " << address;
string sRequestURL = "/api?module=account&action="+type+"&address=";
sRequestURL += address;
http_client apiclient("https://api.etherscan.io");
m_currentRequestCacheFilename = getCacheFilenameForAddress(address,type);
apiclient.request(methods::GET,sRequestURL).then([](http_response response)
{
if (response.status_code() == status_codes::OK)
{
ldebug << "Etherscan : response OK.";
return response.extract_json();
}
return pplx::task_from_result(json::value());
})
.then([this](pplx::task<json::value> previousTask)
{
if (previousTask.get() != json::value::null())
{
linfo << "Etherscan : saving query result to " << m_currentRequestCacheFilename;
ofstream f;
f.open(m_currentRequestCacheFilename);
f << previousTask.get();
f.close();
}
else
{
lerr << "Etherscan : query result is empty. Nothing will be saved to the cache file.";
}
})
.wait();
}
catch(const http::http_exception& e)
{
lerr << "Etherscan : failed to query API about " << address;
}
}
int BlockchainDataSourceETH_Etherscan::GetCoinIdFromContractAddress(string contractAddress)
{
// LPT - Livepeer Token
if (contractAddress == "0x58b6a8a3302369daec383334672404ee733ab239")
return CPFM_COIN_ID_LPT;
// LEND - EthLend - Migrated to AAVE.
if (contractAddress == "0x80fb784b7ed66730e8b1dbd9820afd29931aab03")
return CPFM_COIN_ID_LEND;
// AAVE
if (contractAddress == "0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9")
return CPFM_COIN_ID_AAVE;
// BCDT - Blockchain Certified Data Token.
if (contractAddress == "0xacfa209fb73bf3dd5bbfb1101b9bc999c49062a5")
return CPFM_COIN_ID_BCDT;
// KNC - Kyber Network
if (contractAddress == "0xdd974d5c2e2928dea5f71b9825b8b646686bd200")
return CPFM_COIN_ID_KNC;
// AIR - AirToken
if (contractAddress == "0x27dce1ec4d3f72c3e457cc50354f1f975ddef488")
return CPFM_COIN_ID_AIR;
lerr << "Unsupported contact address " << contractAddress;
return 0;
}

View File

@ -1,42 +0,0 @@
/*
* Copyright (c) 2021, 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/>.
*
*/
#ifndef CPFM_DATASOURCE_ETHERSCAN_H_INCLUDED
#define CPFM_DATASOURCE_ETHERSCAN_H_INCLUDED
#include "datasources/datasource.h"
class BlockchainDataSourceETH_Etherscan
{
public:
list<BlockchainTxDetailsTypeETH> getTxDetailsListForAddress(string address);
private:
list<BlockchainTxDetailsTypeETH> getTxDetailsListForAddressTxType(string address, string type, list<string>& hashList);
void saveBlockchainAddressDataToCacheFile(string address, string type);
string getCacheFilenameForAddress(string address, string type);
bool retrieveCacheFilesForAddress(string address);
int GetCoinIdFromContractAddress(string contractAddress);
string m_currentRequestCacheFilename;
};
#endif // CPFM_DATASOURCE_ETHERSCAN_H_INCLUDED

View File

@ -47,10 +47,10 @@ void ExchangeHandlerKraken::analyzeUserData()
getAccountBalance();
analyzeAccountBalance();
getAccountTrades();
//getAccountTrades();
//analyzeAccountTrades();
getAccountLedgers();
//getAccountLedgers();
//analyzeAccountLedgers();
}
@ -222,13 +222,14 @@ vector<unsigned char> ExchangeHandlerKraken::hmac_sha512(const vector<unsigned c
unsigned int len = EVP_MAX_MD_SIZE;
vector<unsigned char> digest(len);
HMAC_CTX* ctx = HMAC_CTX_new();
HMAC_CTX ctx;
HMAC_CTX_init(&ctx);
HMAC_Init_ex(ctx, key.data(), key.size(), EVP_sha512(), NULL);
HMAC_Update(ctx, data.data(), data.size());
HMAC_Final(ctx, digest.data(), &len);
HMAC_Init_ex(&ctx, key.data(), key.size(), EVP_sha512(), NULL);
HMAC_Update(&ctx, data.data(), data.size());
HMAC_Final(&ctx, digest.data(), &len);
HMAC_CTX_free(ctx);
HMAC_CTX_cleanup(&ctx);
return digest;
}

View File

@ -23,7 +23,7 @@
#include <chrono>
#include "log.h"
using namespace cpfm;
using namespace farm;
using namespace log;
const char* ErrorLogger::name() { return "ERROR"; }
@ -34,8 +34,6 @@ const char* InfoLogger::name() { return "INFO "; }
const char* InfoLogger::color() { return L_White; }
const char* DebugLogger::name() { return "DEBUG"; }
const char* DebugLogger::color() { return L_Teal; }
const char* TraceLogger::name() { return "TRACE"; }
const char* TraceLogger::color() { return L_Violet; }
LoggerBase::LoggerBase()
{

View File

@ -28,14 +28,13 @@
using namespace std;
#define llog(X) cpfm::log::Logger<X>()
#define lerr llog(cpfm::log::ErrorLogger)
#define linfo llog(cpfm::log::InfoLogger)
#define lwarn llog(cpfm::log::WarningLogger)
#define ldebug llog(cpfm::log::DebugLogger)
#define ltrace llog(cpfm::log::TraceLogger)
#define llog(X) farm::log::Logger<X>()
#define lerr llog(farm::log::ErrorLogger)
#define linfo llog(farm::log::InfoLogger)
#define lwarn llog(farm::log::WarningLogger)
#define ldebug llog(farm::log::DebugLogger)
namespace cpfm { namespace log {
namespace farm { namespace log {
#define L_Reset "\x1b[0m" // Text Reset
@ -108,7 +107,6 @@ struct ErrorLogger: public DefaultLogger { static const char* name(); static con
struct WarningLogger: public DefaultLogger { static const char* name(); static const char* color(); };
struct InfoLogger: public DefaultLogger { static const char* name(); static const char* color(); };
struct DebugLogger: public DefaultLogger { static const char* name(); static const char* color(); };
struct TraceLogger: public DefaultLogger { static const char* name(); static const char* color(); };
class LoggerBase
{

View File

@ -23,7 +23,7 @@
Money::Money ()
{
m_amount = 0;
}
Money::Money (bmp::mpf_float_50 x)

View File

@ -54,7 +54,7 @@ public:
Money& operator/=(const Money& x);
friend Money operator/(Money a, const Money& b) { a /= b; return a; }
//Money operator-() { return Money(-m_amount); }
Money operator-() { return Money(-m_amount); }
friend bool operator==(const Money& a, const int& b);
friend bool operator!=(const Money& a, const int& b) { return !operator==(a,b); }

View File

@ -40,8 +40,8 @@ void PortfolioManager::doTestStuff()
// Test user 1
emptyUserBalances(1);
// ExchangesManager exchangesManager(1);
// exchangesManager.analyzeUserAccounts();
ExchangesManager exchangesManager(1);
exchangesManager.analyzeUserAccounts();
WalletsManager walletsManager(1);
walletsManager.analyzeUserWallets();
@ -49,7 +49,6 @@ void PortfolioManager::doTestStuff()
displayUserBalances(1);
// Test user 2
/*
emptyUserBalances(2);
ExchangesManager exchangesManagerTwo(2);
@ -59,7 +58,6 @@ void PortfolioManager::doTestStuff()
walletsManagerTwo.analyzeUserWallets();
displayUserBalances(2);
*/
}
void PortfolioManager::emptyUserBalances(int userId)
@ -157,24 +155,6 @@ string getCoinName(int coinId)
case CPFM_COIN_ID_BTG:
s = "BTG";
break;
case CPFM_COIN_ID_LPT:
s = "LPT";
break;
case CPFM_COIN_ID_BCDT:
s = "BCDT";
break;
case CPFM_COIN_ID_AIR:
s = "AIR";
break;
case CPFM_COIN_ID_AAVE:
s = "AAVE";
break;
case CPFM_COIN_ID_LEND:
s = "LEND";
break;
case CPFM_COIN_ID_KNC:
s = "KNC";
break;
default:
s = "Error!";
break;

View File

@ -39,31 +39,14 @@
#include "log.h"
#include "money.h"
#define CPFM_COIN_ID_BTC 1
#define CPFM_COIN_ID_LTC 2
#define CPFM_COIN_ID_EUR 3
#define CPFM_COIN_ID_ICN 4
#define CPFM_COIN_ID_ETC 5
#define CPFM_COIN_ID_ETH 6
#define CPFM_COIN_ID_BCH 7
#define CPFM_COIN_ID_BTG 8
#define CPFM_COIN_ID_KNC 9
#define CPFM_COIN_ID_AIR 10
#define CPFM_COIN_ID_LEND 11
#define CPFM_COIN_ID_AAVE 12
#define CPFM_COIN_ID_LPT 13
#define CPFM_COIN_ID_BCDT 14
class Time
{
public:
void setFromUnixTime(time_t unixTime) { m_unixTime = unixTime; }
const time_t toUnixTime() const { return m_unixTime; }
private:
time_t m_unixTime;
//date::sys_time<std::chrono::milliseconds>
};
#define CPFM_COIN_ID_BTC 1
#define CPFM_COIN_ID_LTC 2
#define CPFM_COIN_ID_EUR 3
#define CPFM_COIN_ID_ICN 4
#define CPFM_COIN_ID_ETC 5
#define CPFM_COIN_ID_ETH 6
#define CPFM_COIN_ID_BCH 7
#define CPFM_COIN_ID_BTG 8
namespace bmp = boost::multiprecision;
namespace bfs = boost::filesystem;

View File

@ -96,13 +96,13 @@ Money PriceSourceCryptoWatch::getCoinPriceFromData(int coinId)
{
case CPFM_COIN_ID_BTC:
{
Money x (jvalue["result"]["market:kraken:btceur"]);
Money x (jvalue["result"]["kraken:btceur"]);
m = x;
break;
}
case CPFM_COIN_ID_LTC:
{
Money x (jvalue["result"]["market:kraken:ltceur"]);
Money x (jvalue["result"]["kraken:ltceur"]);
m = x;
break;
}
@ -113,79 +113,38 @@ Money PriceSourceCryptoWatch::getCoinPriceFromData(int coinId)
}
case CPFM_COIN_ID_ICN:
{
Money x (jvalue["result"]["market:kraken:icnbtc"]);
Money y (jvalue["result"]["market:kraken:btceur"]);
// ldebug << "ICN is not quoted in EUR. BTC price is : " << x;
Money x (jvalue["result"]["kraken:icnbtc"]);
Money y (jvalue["result"]["kraken:btceur"]);
ldebug << "ICN is not quoted in EUR. BTC price is : " << x;
m = x*y;
break;
}
case CPFM_COIN_ID_ETC:
{
Money x (jvalue["result"]["market:kraken:etceur"]);
Money x (jvalue["result"]["kraken:etceur"]);
m = x;
break;
}
case CPFM_COIN_ID_ETH:
{
Money x (jvalue["result"]["market:kraken:etheur"]);
Money x (jvalue["result"]["kraken:etheur"]);
m = x;
break;
}
case CPFM_COIN_ID_BCH:
{
Money x (jvalue["result"]["market:kraken:bcheur"]);
Money x (jvalue["result"]["kraken:bcheur"]);
m = x;
break;
}
case CPFM_COIN_ID_BTG:
{
Money x (jvalue["result"]["market:bitfinex:btgbtc"]);
Money y (jvalue["result"]["market:kraken:btceur"]);
// ldebug << "BTG is not quoted in EUR. BTC price is : " << x;
Money x (jvalue["result"]["bitfinex:btgbtc"]);
Money y (jvalue["result"]["kraken:btceur"]);
ldebug << "BTG is not quoted in EUR. BTC price is : " << x;
m = x*y;
break;
}
case CPFM_COIN_ID_KNC:
{
Money x (jvalue["result"]["market:binance:kncbtc"]);
Money y (jvalue["result"]["market:kraken:btceur"]);
// ldebug << "KNC is not quoted in EUR. BTC price is : " << x;
m = x*y;
break;
}
case CPFM_COIN_ID_AIR:
{
// ldebug << "AIR is not quoted. Returning 0.";
m = 0;
break;
}
case CPFM_COIN_ID_LEND:
{
Money x (jvalue["result"]["market:kraken:aaveeur"]);
// ldebug << "LEND is not quoted. Using AAVE.";
m = x/100;
break;
}
case CPFM_COIN_ID_AAVE:
{
Money x (jvalue["result"]["market:kraken:aaveeur"]);
m = x;
break;
}
case CPFM_COIN_ID_LPT:
{
Money x (jvalue["result"]["market:poloniex:lptbtc"]);
Money y (jvalue["result"]["market:kraken:btceur"]);
// ldebug << "LPT is not quoted in EUR. BTC price is : " << x;
m = x*y;
break;
}
case CPFM_COIN_ID_BCDT:
{
// ldebug << "BCDT is not quoted. Returning 0.";
m = 0;
break;
}
default:
{
m = 0;

838
src/sql.h
View File

@ -216,32 +216,9 @@ namespace TableWallets_
};
using _traits = sqlpp::make_traits<sqlpp::varchar>;
};
struct Active
{
struct _alias_t
{
static constexpr const char _literal[] = "active";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T active;
T& operator()()
{
return active;
}
const T& operator()() const
{
return active;
}
};
};
using _traits = sqlpp::make_traits<sqlpp::boolean>;
};
}
struct TableWallets : sqlpp::table_t<TableWallets,TableWallets_::WalletId,TableWallets_::UserId,TableWallets_::TypeId,TableWallets_::WalletName,TableWallets_::Active>
struct TableWallets : sqlpp::table_t<TableWallets,TableWallets_::WalletId,TableWallets_::UserId,TableWallets_::TypeId,TableWallets_::WalletName>
{
using _value_type = sqlpp::no_value_t;
struct _alias_t
@ -453,6 +430,29 @@ namespace TableWalletsTx_
using _traits = sqlpp::make_traits<sqlpp::bigint>;
};
struct BlockchainTxId
{
struct _alias_t
{
static constexpr const char _literal[] = "blockchain_tx_id";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T blockchain_tx_id;
T& operator()()
{
return blockchain_tx_id;
}
const T& operator()() const
{
return blockchain_tx_id;
}
};
};
using _traits = sqlpp::make_traits<sqlpp::varchar>;
};
struct Amount
{
struct _alias_t
@ -499,92 +499,23 @@ namespace TableWalletsTx_
using _traits = sqlpp::make_traits<sqlpp::decimal>;
};
struct AmountCoinId
struct Timestamp
{
struct _alias_t
{
static constexpr const char _literal[] = "amount_coin_id";
static constexpr const char _literal[] = "timestamp";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T amount_coin_id;
T timestamp;
T& operator()()
{
return amount_coin_id;
return timestamp;
}
const T& operator()() const
{
return amount_coin_id;
}
};
};
using _traits = sqlpp::make_traits<sqlpp::bigint>;
};
struct FeeCoinId
{
struct _alias_t
{
static constexpr const char _literal[] = "fee_coin_id";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T fee_coin_id;
T& operator()()
{
return fee_coin_id;
}
const T& operator()() const
{
return fee_coin_id;
}
};
};
using _traits = sqlpp::make_traits<sqlpp::bigint>;
};
struct UnixTime
{
struct _alias_t
{
static constexpr const char _literal[] = "unix_time";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T unix_time;
T& operator()()
{
return unix_time;
}
const T& operator()() const
{
return unix_time;
}
};
};
using _traits = sqlpp::make_traits<sqlpp::bigint>;
};
struct OperationType
{
struct _alias_t
{
static constexpr const char _literal[] = "operation_type";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T operation_type;
T& operator()()
{
return operation_type;
}
const T& operator()() const
{
return operation_type;
return timestamp;
}
};
};
@ -592,7 +523,7 @@ namespace TableWalletsTx_
};
}
struct TableWalletsTx : sqlpp::table_t<TableWalletsTx,TableWalletsTx_::TxId,TableWalletsTx_::WalletId,TableWalletsTx_::Amount,TableWalletsTx_::Fee,TableWalletsTx_::AmountCoinId,TableWalletsTx_::FeeCoinId,TableWalletsTx_::UnixTime,TableWalletsTx_::OperationType>
struct TableWalletsTx : sqlpp::table_t<TableWalletsTx,TableWalletsTx_::TxId,TableWalletsTx_::WalletId,TableWalletsTx_::BlockchainTxId,TableWalletsTx_::Amount,TableWalletsTx_::Fee,TableWalletsTx_::Timestamp>
{
using _value_type = sqlpp::no_value_t;
struct _alias_t
@ -615,691 +546,6 @@ struct TableWalletsTx : sqlpp::table_t<TableWalletsTx,TableWalletsTx_::TxId,Tabl
};
};
namespace TableWalletsBtcRawTx_
{
struct RawTxId
{
struct _alias_t
{
static constexpr const char _literal[] = "raw_tx_id";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T raw_tx_id;
T& operator()()
{
return raw_tx_id;
}
const T& operator()() const
{
return raw_tx_id;
}
};
};
using _traits = sqlpp::make_traits<sqlpp::bigint>;
};
struct WalletId
{
struct _alias_t
{
static constexpr const char _literal[] = "wallet_id";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T wallet_id;
T& operator()()
{
return wallet_id;
}
const T& operator()() const
{
return wallet_id;
}
};
};
using _traits = sqlpp::make_traits<sqlpp::bigint>;
};
}
struct TableWalletsBtcRawTx : sqlpp::table_t<TableWalletsBtcRawTx,TableWalletsBtcRawTx_::RawTxId,TableWalletsBtcRawTx_::WalletId>
{
using _value_type = sqlpp::no_value_t;
struct _alias_t
{
static constexpr const char _literal[] = "wallets_btc_raw_tx";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T wallets_btc_raw_tx;
T& operator()()
{
return wallets_btc_raw_tx;
}
const T& operator()() const
{
return wallets_btc_raw_tx;
}
};
};
};
namespace TableWalletsEthRawTx_
{
struct RawTxId
{
struct _alias_t
{
static constexpr const char _literal[] = "raw_tx_id";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T raw_tx_id;
T& operator()()
{
return raw_tx_id;
}
const T& operator()() const
{
return raw_tx_id;
}
};
};
using _traits = sqlpp::make_traits<sqlpp::bigint>;
};
struct WalletId
{
struct _alias_t
{
static constexpr const char _literal[] = "wallet_id";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T wallet_id;
T& operator()()
{
return wallet_id;
}
const T& operator()() const
{
return wallet_id;
}
};
};
using _traits = sqlpp::make_traits<sqlpp::bigint>;
};
}
struct TableWalletsEthRawTx : sqlpp::table_t<TableWalletsEthRawTx,TableWalletsEthRawTx_::RawTxId,TableWalletsEthRawTx_::WalletId>
{
using _value_type = sqlpp::no_value_t;
struct _alias_t
{
static constexpr const char _literal[] = "wallets_eth_raw_tx";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T wallets_eth_raw_tx;
T& operator()()
{
return wallets_eth_raw_tx;
}
const T& operator()() const
{
return wallets_eth_raw_tx;
}
};
};
};
namespace TableBlockchainBtcRawTx_
{
struct RawTxId
{
struct _alias_t
{
static constexpr const char _literal[] = "raw_tx_id";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T raw_tx_id;
T& operator()()
{
return raw_tx_id;
}
const T& operator()() const
{
return raw_tx_id;
}
};
};
using _traits = sqlpp::make_traits<sqlpp::bigint>;
};
struct BlockchainId
{
struct _alias_t
{
static constexpr const char _literal[] = "blockchain_id";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T blockchain_id;
T& operator()()
{
return blockchain_id;
}
const T& operator()() const
{
return blockchain_id;
}
};
};
using _traits = sqlpp::make_traits<sqlpp::bigint>;
};
struct Hash
{
struct _alias_t
{
static constexpr const char _literal[] = "hash";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T hash;
T& operator()()
{
return hash;
}
const T& operator()() const
{
return hash;
}
};
};
using _traits = sqlpp::make_traits<sqlpp::varchar>;
};
struct UnixTime
{
struct _alias_t
{
static constexpr const char _literal[] = "unix_time";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T unix_time;
T& operator()()
{
return unix_time;
}
const T& operator()() const
{
return unix_time;
}
};
};
using _traits = sqlpp::make_traits<sqlpp::bigint>;
};
}
struct TableBlockchainBtcRawTx : sqlpp::table_t<TableBlockchainBtcRawTx,TableBlockchainBtcRawTx_::RawTxId,TableBlockchainBtcRawTx_::BlockchainId,TableBlockchainBtcRawTx_::Hash,TableBlockchainBtcRawTx_::UnixTime>
{
using _value_type = sqlpp::no_value_t;
struct _alias_t
{
static constexpr const char _literal[] = "blockchain_btc_raw_tx";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T blockchain_btc_raw_tx;
T& operator()()
{
return blockchain_btc_raw_tx;
}
const T& operator()() const
{
return blockchain_btc_raw_tx;
}
};
};
};
namespace TableBlockchainBtcRawTxDetails_
{
struct RawTxDetailId
{
struct _alias_t
{
static constexpr const char _literal[] = "raw_tx_detail_id";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T raw_tx_detail_id;
T& operator()()
{
return raw_tx_detail_id;
}
const T& operator()() const
{
return raw_tx_detail_id;
}
};
};
using _traits = sqlpp::make_traits<sqlpp::bigint>;
};
struct RawTxId
{
struct _alias_t
{
static constexpr const char _literal[] = "raw_tx_id";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T raw_tx_id;
T& operator()()
{
return raw_tx_id;
}
const T& operator()() const
{
return raw_tx_id;
}
};
};
using _traits = sqlpp::make_traits<sqlpp::bigint>;
};
struct Amount
{
struct _alias_t
{
static constexpr const char _literal[] = "amount";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T amount;
T& operator()()
{
return amount;
}
const T& operator()() const
{
return amount;
}
};
};
using _traits = sqlpp::make_traits<sqlpp::decimal>;
};
struct Address
{
struct _alias_t
{
static constexpr const char _literal[] = "address";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T address;
T& operator()()
{
return address;
}
const T& operator()() const
{
return address;
}
};
};
using _traits = sqlpp::make_traits<sqlpp::varchar>;
};
}
struct TableBlockchainBtcRawTxDetails : sqlpp::table_t<TableBlockchainBtcRawTxDetails,TableBlockchainBtcRawTxDetails_::RawTxDetailId,TableBlockchainBtcRawTxDetails_::RawTxId,TableBlockchainBtcRawTxDetails_::Amount,TableBlockchainBtcRawTxDetails_::Address>
{
using _value_type = sqlpp::no_value_t;
struct _alias_t
{
static constexpr const char _literal[] = "blockchain_btc_raw_tx_details";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T blockchain_btc_raw_tx_details;
T& operator()()
{
return blockchain_btc_raw_tx_details;
}
const T& operator()() const
{
return blockchain_btc_raw_tx_details;
}
};
};
};
namespace TableBlockchainEthRawTx_
{
struct RawTxId
{
struct _alias_t
{
static constexpr const char _literal[] = "raw_tx_id";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T raw_tx_id;
T& operator()()
{
return raw_tx_id;
}
const T& operator()() const
{
return raw_tx_id;
}
};
};
using _traits = sqlpp::make_traits<sqlpp::bigint>;
};
struct BlockchainId
{
struct _alias_t
{
static constexpr const char _literal[] = "blockchain_id";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T blockchain_id;
T& operator()()
{
return blockchain_id;
}
const T& operator()() const
{
return blockchain_id;
}
};
};
using _traits = sqlpp::make_traits<sqlpp::bigint>;
};
struct Hash
{
struct _alias_t
{
static constexpr const char _literal[] = "hash";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T hash;
T& operator()()
{
return hash;
}
const T& operator()() const
{
return hash;
}
};
};
using _traits = sqlpp::make_traits<sqlpp::varchar>;
};
struct UnixTime
{
struct _alias_t
{
static constexpr const char _literal[] = "unix_time";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T unix_time;
T& operator()()
{
return unix_time;
}
const T& operator()() const
{
return unix_time;
}
};
};
using _traits = sqlpp::make_traits<sqlpp::bigint>;
};
struct Fee
{
struct _alias_t
{
static constexpr const char _literal[] = "fee";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T fee;
T& operator()()
{
return fee;
}
const T& operator()() const
{
return fee;
}
};
};
using _traits = sqlpp::make_traits<sqlpp::decimal>;
};
}
struct TableBlockchainEthRawTx : sqlpp::table_t<TableBlockchainEthRawTx,TableBlockchainEthRawTx_::RawTxId,TableBlockchainEthRawTx_::BlockchainId,TableBlockchainEthRawTx_::Hash,TableBlockchainEthRawTx_::UnixTime,TableBlockchainEthRawTx_::Fee>
{
using _value_type = sqlpp::no_value_t;
struct _alias_t
{
static constexpr const char _literal[] = "blockchain_eth_raw_tx";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T blockchain_eth_raw_tx;
T& operator()()
{
return blockchain_eth_raw_tx;
}
const T& operator()() const
{
return blockchain_eth_raw_tx;
}
};
};
};
namespace TableBlockchainEthRawTxDetails_
{
struct RawTxDetailId
{
struct _alias_t
{
static constexpr const char _literal[] = "raw_tx_detail_id";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T raw_tx_detail_id;
T& operator()()
{
return raw_tx_detail_id;
}
const T& operator()() const
{
return raw_tx_detail_id;
}
};
};
using _traits = sqlpp::make_traits<sqlpp::bigint>;
};
struct RawTxId
{
struct _alias_t
{
static constexpr const char _literal[] = "raw_tx_id";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T raw_tx_id;
T& operator()()
{
return raw_tx_id;
}
const T& operator()() const
{
return raw_tx_id;
}
};
};
using _traits = sqlpp::make_traits<sqlpp::bigint>;
};
struct AddressFrom
{
struct _alias_t
{
static constexpr const char _literal[] = "address_from";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T address_from;
T& operator()()
{
return address_from;
}
const T& operator()() const
{
return address_from;
}
};
};
using _traits = sqlpp::make_traits<sqlpp::varchar>;
};
struct AddressTo
{
struct _alias_t
{
static constexpr const char _literal[] = "address_to";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T address_to;
T& operator()()
{
return address_to;
}
const T& operator()() const
{
return address_to;
}
};
};
using _traits = sqlpp::make_traits<sqlpp::varchar>;
};
struct AmountCoinId
{
struct _alias_t
{
static constexpr const char _literal[] = "amount_coin_id";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T amount_coin_id;
T& operator()()
{
return amount_coin_id;
}
const T& operator()() const
{
return amount_coin_id;
}
};
};
using _traits = sqlpp::make_traits<sqlpp::bigint>;
};
struct Amount
{
struct _alias_t
{
static constexpr const char _literal[] = "amount";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T amount;
T& operator()()
{
return amount;
}
const T& operator()() const
{
return amount;
}
};
};
using _traits = sqlpp::make_traits<sqlpp::decimal>;
};
};
struct TableBlockchainEthRawTxDetails : sqlpp::table_t<TableBlockchainEthRawTxDetails,TableBlockchainEthRawTxDetails_::RawTxDetailId,TableBlockchainEthRawTxDetails_::RawTxId,TableBlockchainEthRawTxDetails_::AddressFrom,TableBlockchainEthRawTxDetails_::AddressTo,TableBlockchainEthRawTxDetails_::AmountCoinId,TableBlockchainEthRawTxDetails_::Amount>
{
using _value_type = sqlpp::no_value_t;
struct _alias_t
{
static constexpr const char _literal[] = "blockchain_eth_raw_tx_details";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T blockchain_eth_raw_tx_details;
T& operator()()
{
return blockchain_eth_raw_tx_details;
}
const T& operator()() const
{
return blockchain_eth_raw_tx_details;
}
};
};
};
namespace TableExchangesLedgers_
{
struct LedgerId
@ -1417,23 +663,23 @@ namespace TableExchangesLedgers_
using _traits = sqlpp::make_traits<sqlpp::varchar>;
};
struct UnixTime
struct Timestamp
{
struct _alias_t
{
static constexpr const char _literal[] = "unix_time";
static constexpr const char _literal[] = "timestamp";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T unix_time;
T timestamp;
T& operator()()
{
return unix_time;
return timestamp;
}
const T& operator()() const
{
return unix_time;
return timestamp;
}
};
};
@ -1510,7 +756,7 @@ namespace TableExchangesLedgers_
};
}
struct TableExchangesLedgers : sqlpp::table_t<TableExchangesLedgers,TableExchangesLedgers_::LedgerId,TableExchangesLedgers_::AccountId,TableExchangesLedgers_::ExchangeLedgerId,TableExchangesLedgers_::ExchangeReferenceId,TableExchangesLedgers_::UnixTime,TableExchangesLedgers_::OperationType,TableExchangesLedgers_::CoinId,TableExchangesLedgers_::Amount,TableExchangesLedgers_::Fee>
struct TableExchangesLedgers : sqlpp::table_t<TableExchangesLedgers,TableExchangesLedgers_::LedgerId,TableExchangesLedgers_::AccountId,TableExchangesLedgers_::ExchangeLedgerId,TableExchangesLedgers_::ExchangeReferenceId,TableExchangesLedgers_::Timestamp,TableExchangesLedgers_::OperationType,TableExchangesLedgers_::CoinId,TableExchangesLedgers_::Amount,TableExchangesLedgers_::Fee>
{
using _value_type = sqlpp::no_value_t;
struct _alias_t
@ -1673,23 +919,23 @@ namespace TableExchangesTrades_
using _traits = sqlpp::make_traits<sqlpp::bigint>;
};
struct UnixTime
struct Timestamp
{
struct _alias_t
{
static constexpr const char _literal[] = "unix_time";
static constexpr const char _literal[] = "timestamp";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template <typename T>
struct _member_t
{
T unix_time;
T timestamp;
T& operator()()
{
return unix_time;
return timestamp;
}
const T& operator()() const
{
return unix_time;
return timestamp;
}
};
};
@ -1858,7 +1104,7 @@ namespace TableExchangesTrades_
};
}
struct TableExchangesTrades : sqlpp::table_t<TableExchangesTrades,TableExchangesTrades_::TradeId,TableExchangesTrades_::AccountId,TableExchangesTrades_::ExchangeTradeId,TableExchangesTrades_::ExchangeOrderId,TableExchangesTrades_::BaseCoinId,TableExchangesTrades_::QuoteCoinId,TableExchangesTrades_::UnixTime,TableExchangesTrades_::Type,TableExchangesTrades_::OrderType,TableExchangesTrades_::Price,TableExchangesTrades_::Cost,TableExchangesTrades_::Fee,TableExchangesTrades_::Volume,TableExchangesTrades_::FeeCoinId>
struct TableExchangesTrades : sqlpp::table_t<TableExchangesTrades,TableExchangesTrades_::TradeId,TableExchangesTrades_::AccountId,TableExchangesTrades_::ExchangeTradeId,TableExchangesTrades_::ExchangeOrderId,TableExchangesTrades_::BaseCoinId,TableExchangesTrades_::QuoteCoinId,TableExchangesTrades_::Timestamp,TableExchangesTrades_::Type,TableExchangesTrades_::OrderType,TableExchangesTrades_::Price,TableExchangesTrades_::Cost,TableExchangesTrades_::Fee,TableExchangesTrades_::Volume,TableExchangesTrades_::FeeCoinId>
{
using _value_type = sqlpp::no_value_t;
struct _alias_t

View File

@ -19,32 +19,9 @@
*/
#include "wallet.h"
#include "wallets/wallet_btc.h"
#include "wallets/wallet_bch.h"
#include "wallets/wallet_eth.h"
string getBlockchainName(int blockchainId)
{
string s;
switch(blockchainId)
{
case CPFM_BLOCKCHAIN_ID_BTC:
s = "BTC";
break;
case CPFM_BLOCKCHAIN_ID_BCH:
s = "BCH";
break;
case CPFM_BLOCKCHAIN_ID_ETH:
s = "ETH";
break;
default:
s = "Error!";
break;
}
return s;
}
#include "wallets/btc.h"
#include "wallets/bch.h"
#include "wallets/eth.h"
WalletsManager::WalletsManager(int userId)
{
@ -61,34 +38,38 @@ void WalletsManager::analyzeUserWallets()
mysql::connection db(getMysqlConfig());
const auto wallets = TableWallets{};
list<Wallet*> userWallets;
for (const auto& row: db.run(select(wallets.type_id,wallets.wallet_id).from(wallets).where(wallets.user_id == m_userId and wallets.active == true)))
// Identify coins, we need one analyzer object per coin.
list<WalletHandler*> handlers;
for (const auto& row: db.run(select(wallets.type_id).from(wallets).where(wallets.user_id == m_userId).group_by(wallets.type_id)))
{
Wallet* wallet;
switch (row.type_id)
int typeId = row.type_id;
WalletHandler* walletHandler;
switch (typeId)
{
case CPFM_WALLET_TYPE_ID_BTC:
wallet = new WalletBTC(row.wallet_id);
userWallets.push_back(wallet);
walletHandler = new WalletHandlerBTC(m_userId);
handlers.push_back(walletHandler);
break;
case CPFM_WALLET_TYPE_ID_BCH:
wallet = new WalletBCH(row.wallet_id);
userWallets.push_back(wallet);
walletHandler = new WalletHandlerBCH(m_userId);
handlers.push_back(walletHandler);
break;
case CPFM_WALLET_TYPE_ID_ETH:
wallet = new WalletETH(row.wallet_id);
userWallets.push_back(wallet);
walletHandler = new WalletHandlerETH(m_userId);
handlers.push_back(walletHandler);
break;
default:
lerr << "Unsupported wallet type: " << row.type_id;
lerr << "Unsupported wallet type: " << typeId;
break;
}
}
for (auto const& wallet: userWallets)
emptyWalletsTx();
for (auto const& handler: handlers)
{
wallet->update();
delete wallet;
handler->analyzeUserWallets();
delete handler;
}
}
@ -104,16 +85,20 @@ void WalletsManager::emptyWalletsTx()
userWallets.push_back(row.wallet_id);
}
for (const auto& idWallet: userWallets)
for (const auto& id: userWallets)
{
list<int> userWalletTx;
for (const auto& row: db.run(select(wallets_tx.tx_id).from(wallets_tx).where(wallets_tx.wallet_id == idWallet)))
{
userWalletTx.push_back(row.tx_id);
}
db.run(remove_from(wallets_tx).where(wallets_tx.wallet_id == idWallet));
db.run(remove_from(wallets_tx).where(wallets_tx.wallet_id == id));
}
linfo << "Emptied Wallets Tx DB table for user " << m_userId;
}
WalletHandler::WalletHandler(int userId)
{
m_userId = userId;
}
WalletHandler::~WalletHandler()
{
}

View File

@ -27,17 +27,6 @@
#define CPFM_WALLET_TYPE_ID_ETH 2
#define CPFM_WALLET_TYPE_ID_BCH 3
#define CPFM_WALLET_OPERATION_UNKNOWN 0
#define CPFM_WALLET_OPERATION_MINING_MINTED 1
#define CPFM_WALLET_OPERATION_MINING_POOL 2
#define CPFM_WALLET_OPERATION_TRANSFERT 3
#define CPFM_BLOCKCHAIN_ID_BTC 1
#define CPFM_BLOCKCHAIN_ID_BCH 2
#define CPFM_BLOCKCHAIN_ID_ETH 3
string getBlockchainName(int blockchainId);
class WalletsManager
{
public:
@ -52,15 +41,16 @@ private:
int m_userId;
};
class Wallet
class WalletHandler
{
public:
Wallet (int walletId) { m_walletId = walletId; }
WalletHandler(int userId);
virtual ~WalletHandler();
virtual void update() = 0;
virtual void analyzeUserWallets() = 0;
protected:
int m_walletId;
int m_userId;
};
#endif // CPFM_WALLET_H_INCLUDED

316
src/wallets/bch.cpp Normal file
View File

@ -0,0 +1,316 @@
/*
* 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/bch.h"
WalletHandlerBCH::WalletHandlerBCH(int userId) : WalletHandler(userId)
{
}
WalletHandlerBCH::~WalletHandlerBCH()
{
}
void WalletHandlerBCH::analyzeUserWallets()
{
linfo << "Analyzing user " << m_userId << " BCH 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_BCH)))
{
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 WalletHandlerBCH::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 blockhain tx id 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_BCH, wallets_balances.balance = walletBalance.toBoostMpf()));
}
void WalletHandlerBCH::addUserWalletDataToDB(int walletId, list<string> walletAddresses, list<string> userAddresses)
{
linfo << "Analyzing wallet " << walletId << ".";
for (const auto &a : walletAddresses)
{
AddressHandlerBCH addr(a, walletId);
addr.setWalletAddresses(walletAddresses);
addr.setUserAddresses(userAddresses);
addr.addAddressDataToDB();
}
}
AddressHandlerBCH::AddressHandlerBCH(string address, int walletId)
{
m_address = address;
m_walletId = walletId;
}
AddressHandlerBCH::~AddressHandlerBCH()
{
}
string AddressHandlerBCH::getCacheFilename()
{
string cacheFilename("data/cache/bch/" + m_address);
return cacheFilename;
}
void AddressHandlerBCH::getAddressData()
{
linfo << "Getting data for address: " << m_address << ".";
if (!bfs::exists(getCacheFilename()))
{
getBlockchainAddressData();
}
}
void AddressHandlerBCH::addAddressDataToDB()
{
linfo << "Analyzing data for address: " << m_address << ".";
getAddressData();
addCachedAddressDataToDB();
}
void AddressHandlerBCH::getBlockchainAddressData()
{
try
{
string sRequestURL = "/insight-api/txs/?address=";
sRequestURL += m_address;
http_client apiclient("https://cashexplorer.bitcoin.com");
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 AddressHandlerBCH::addCachedAddressDataToDB()
{
ifstream f;
f.open(getCacheFilename());
json::value jvalue = json::value::parse(f);
f.close();
addAddressDataJSONToDB(jvalue);
}
void AddressHandlerBCH::addAddressDataJSONToDB(json::value jvalue)
{
mysql::connection db(getMysqlConfig());
const auto wallets_tx = TableWalletsTx{};
string myAddr = m_address;
linfo << "Analyzing address: " << myAddr;
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.blockchain_tx_id = txid, wallets_tx.amount = amount.toBoostMpf(), wallets_tx.fee = fee.toBoostMpf(), wallets_tx.timestamp = timestamp));
}
}

63
src/wallets/bch.h Normal file
View File

@ -0,0 +1,63 @@
/*
* 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/>.
*
*/
#ifndef CPFM_WALLET_BCH_H_INCLUDED
#define CPFM_WALLET_BCH_H_INCLUDED
#include "wallet.h"
class WalletHandlerBCH : public WalletHandler
{
public:
WalletHandlerBCH(int userId);
virtual ~WalletHandlerBCH();
virtual void analyzeUserWallets();
private:
void addUserWalletDataToDB(int walletId, list<string> walletAddresses, list<string> userAddresses);
void analyzeUserWalletData(int walletId);
};
class AddressHandlerBCH
{
public:
AddressHandlerBCH(string address, int walletId);
virtual ~AddressHandlerBCH();
void getAddressData();
void addAddressDataToDB();
void setWalletAddresses(list<string> walletAddresses) { m_walletAddresses = walletAddresses; }
void setUserAddresses(list<string> userAddresses) { m_userAddresses = userAddresses; }
private:
void getBlockchainAddressData();
void addCachedAddressDataToDB();
void addAddressDataJSONToDB(json::value jvalue);
string getCacheFilename();
string m_address;
int m_walletId;
list<string> m_userAddresses;
list<string> m_walletAddresses;
};
#endif // CPFM_WALLET_BTC_H_INCLUDED

332
src/wallets/btc.cpp Normal file
View File

@ -0,0 +1,332 @@
/*
* 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/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<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_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<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_BTC,
wallets_balances.balance = walletBalance.toBoostMpf()
));
}
void WalletHandlerBTC::addUserWalletDataToDB(int walletId, list<string> walletAddresses, list<string> 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<json::value> 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<jvalue["txs"].size();i++)
{
string hash = jvalue["txs"][i]["hash"].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]["inputs"].size();j++)
{
string inputAddr = jvalue["txs"][i]["inputs"][j]["prev_out"]["addr"].as_string();
__int64 amount = jvalue["txs"][i]["inputs"][j]["prev_out"]["value"].as_integer();
txTotalInputAmount += amount;
if (inputAddr == myAddr)
{
bFromMyAddr = true;
txMyInputAmount += amount;
}
else
{
inputAddrList.push_back(inputAddr);
}
}
for (int j=0;j<jvalue["txs"][i]["out"].size();j++)
{
string outputAddr = jvalue["txs"][i]["out"][j]["addr"].as_string();
__int64 amount = jvalue["txs"][i]["out"][j]["value"].as_integer();
txTotalOutputAmount += amount;
if (outputAddr == myAddr)
{
bToMyAddr = true;
txMyOutputAmount += amount;
}
else
{
outputAddrList.push_back(outputAddr);
}
}
Money amount;
Money fee;
if (bFromMyAddr)
{
amount = -txMyInputAmount/100000000;
fee = txTotalInputAmount/100000000 - txTotalOutputAmount/100000000;
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/100000000;
fee = 0;
}
db(insert_into(wallets_tx).set(
wallets_tx.wallet_id = m_walletId,
wallets_tx.blockchain_tx_id = hash,
wallets_tx.amount = amount.toBoostMpf(),
wallets_tx.fee = fee.toBoostMpf(),
wallets_tx.timestamp = timestamp
));
}
}

63
src/wallets/btc.h Normal file
View File

@ -0,0 +1,63 @@
/*
* 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/>.
*
*/
#ifndef CPFM_WALLET_BTC_H_INCLUDED
#define CPFM_WALLET_BTC_H_INCLUDED
#include "wallet.h"
class WalletHandlerBTC : public WalletHandler
{
public:
WalletHandlerBTC(int userId);
virtual ~WalletHandlerBTC();
virtual void analyzeUserWallets();
private:
void addUserWalletDataToDB(int walletId, list<string> walletAddresses, list<string> userAddresses);
void analyzeUserWalletData(int walletId);
};
class AddressHandlerBTC
{
public:
AddressHandlerBTC(string address, int walletId);
virtual ~AddressHandlerBTC();
void getAddressData();
void addAddressDataToDB();
void setWalletAddresses(list<string> walletAddresses) { m_walletAddresses = walletAddresses; }
void setUserAddresses(list<string> userAddresses) { m_userAddresses = userAddresses; }
private:
void getBlockchainAddressData();
void addCachedAddressDataToDB();
void addAddressDataJSONToDB(json::value jvalue);
string getCacheFilename();
string m_address;
int m_walletId;
list<string> m_userAddresses;
list<string> m_walletAddresses;
};
#endif // CPFM_WALLET_BTC_H_INCLUDED

353
src/wallets/eth.cpp Normal file
View File

@ -0,0 +1,353 @@
/*
* 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
));
}
*/
}

63
src/wallets/eth.h Normal file
View File

@ -0,0 +1,63 @@
/*
* 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/>.
*
*/
#ifndef CPFM_WALLET_ETH_H_INCLUDED
#define CPFM_WALLET_ETH_H_INCLUDED
#include "wallet.h"
class WalletHandlerETH : public WalletHandler
{
public:
WalletHandlerETH(int userId);
virtual ~WalletHandlerETH();
virtual void analyzeUserWallets();
private:
void addUserWalletDataToDB(int walletId, list<string> walletAddresses, list<string> userAddresses);
void analyzeUserWalletData(int walletId);
};
class AddressHandlerETH
{
public:
AddressHandlerETH(string address, int walletId);
virtual ~AddressHandlerETH();
void getAddressData();
void addAddressDataToDB();
void setWalletAddresses(list<string> walletAddresses) { m_walletAddresses = walletAddresses; }
void setUserAddresses(list<string> userAddresses) { m_userAddresses = userAddresses; }
private:
void getBlockchainAddressData();
void addCachedAddressDataToDB();
void addAddressDataJSONToDB(json::value jvalue);
string getCacheFilename();
string m_address;
int m_walletId;
list<string> m_userAddresses;
list<string> m_walletAddresses;
};
#endif // CPFM_WALLET_ETH_H_INCLUDED

View File

@ -1,27 +0,0 @@
/*
* 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/wallet_bch.h"
list<BlockchainTxDetailsTypeBTC> WalletBCH::getTxDetailsListForAddresses(list<string> addresses)
{
BlockchainDataSourceBCH src;
return src.getTxDetailsListForAddresses(addresses);
}

View File

@ -1,36 +0,0 @@
/*
* 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/>.
*
*/
#ifndef CPFM_WALLET_BCH_H_INCLUDED
#define CPFM_WALLET_BCH_H_INCLUDED
#include "wallets/wallet_type_btc.h"
#include "datasources/datasource.h"
class WalletBCH : public WalletTypeBTC
{
public:
WalletBCH(int walletId):WalletTypeBTC(walletId) { m_coinId = CPFM_COIN_ID_BCH; m_blockchainId = CPFM_BLOCKCHAIN_ID_BCH; m_walletTypeId = CPFM_WALLET_TYPE_ID_BCH; }
protected:
virtual list<BlockchainTxDetailsTypeBTC> getTxDetailsListForAddresses(list<string> addresses);
};
#endif // CPFM_WALLET_BCH_H_INCLUDED

View File

@ -1,27 +0,0 @@
/*
* 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/wallet_btc.h"
list<BlockchainTxDetailsTypeBTC> WalletBTC::getTxDetailsListForAddresses(list<string> addresses)
{
BlockchainDataSourceBTC src;
return src.getTxDetailsListForAddresses(addresses);
}

View File

@ -1,36 +0,0 @@
/*
* 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/>.
*
*/
#ifndef CPFM_WALLET_BTC_H_INCLUDED
#define CPFM_WALLET_BTC_H_INCLUDED
#include "wallets/wallet_type_btc.h"
#include "datasources/datasource.h"
class WalletBTC : public WalletTypeBTC
{
public:
WalletBTC(int walletId):WalletTypeBTC(walletId) { m_coinId = CPFM_COIN_ID_BTC; m_blockchainId = CPFM_BLOCKCHAIN_ID_BTC; m_walletTypeId = CPFM_WALLET_TYPE_ID_BTC; }
protected:
virtual list<BlockchainTxDetailsTypeBTC> getTxDetailsListForAddresses(list<string> addresses);
};
#endif // CPFM_WALLET_BTC_H_INCLUDED

View File

@ -1,27 +0,0 @@
/*
* 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/wallet_eth.h"
list<BlockchainTxDetailsTypeETH> WalletETH::getTxDetailsListForAddress(string address)
{
BlockchainDataSourceETH src;
return src.getTxDetailsListForAddress(address);
}

View File

@ -1,36 +0,0 @@
/*
* 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/>.
*
*/
#ifndef CPFM_WALLET_ETH_H_INCLUDED
#define CPFM_WALLET_ETH_H_INCLUDED
#include "wallets/wallet_type_eth.h"
#include "datasources/datasource.h"
class WalletETH : public WalletTypeETH
{
public:
WalletETH(int walletId):WalletTypeETH(walletId) { m_coinId = CPFM_COIN_ID_ETH; m_blockchainId = CPFM_BLOCKCHAIN_ID_ETH; m_walletTypeId = CPFM_WALLET_TYPE_ID_ETH; }
protected:
virtual list<BlockchainTxDetailsTypeETH> getTxDetailsListForAddress(string address);
};
#endif // CPFM_WALLET_ETH_H_INCLUDED

View File

@ -1,324 +0,0 @@
/*
* Copyright (c) 2021, 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/wallet_type_btc.h"
SQLPP_ALIAS_PROVIDER(max_raw_tx_id);
void WalletTypeBTC::update()
{
if (!m_walletId)
{
lerr << "Wallet id not defined. Ignoring update.";
}
linfo << "Analyzing " << getCoinName(m_coinId) << " wallet " << m_walletId;
mysql::connection db(getMysqlConfig());
const auto wallets = TableWallets{};
const auto wallets_addresses = TableWalletsAddresses{};
const auto wallets_tx = TableWalletsTx{};
// Load wallet addresses
list<string> addresses;
for (const auto& row: db.run(select(wallets.wallet_id,wallets.type_id,wallets_addresses.address).from(wallets.cross_join(wallets_addresses)).where(wallets.wallet_id == wallets_addresses.wallet_id and wallets.wallet_id == m_walletId and wallets.type_id == m_walletTypeId)))
{
addresses.push_back(row.address);
}
// Get wallet tx data from data source and insert it in the DB raw tables.
list<BlockchainTxDetailsTypeBTC> l;
l = getTxDetailsListForAddresses(addresses);
addTxDetailsListToRawDB(m_walletId,l);
// Load tx data from DB raw tables, analyze data, and insert it in DB tables.
l = getTxDetailsListFromRawDB(m_walletId);
db(remove_from(wallets_tx).where(wallets_tx.wallet_id == m_walletId));
addTxDetailsListToDB(m_walletId,addresses,l);
updateBalanceFromTxDetailsInDB(m_walletId);
}
void WalletTypeBTC::addTxDetailsListToRawDB(const int walletId, const list<BlockchainTxDetailsTypeBTC>& l)
{
mysql::connection db(getMysqlConfig());
const auto wallets_btc_raw_tx = TableWalletsBtcRawTx{};
const auto blockchain_btc_raw_tx = TableBlockchainBtcRawTx{};
const auto blockchain_btc_raw_tx_details = TableBlockchainBtcRawTxDetails{};
linfo << "Clearing tx raw data links for " << getCoinName(m_coinId) << " wallet " << walletId << ".";
const auto result = db(remove_from(wallets_btc_raw_tx).where(wallets_btc_raw_tx.wallet_id == walletId));
linfo << "Now adding raw data to DB for " << getCoinName(m_coinId) << " wallet " << walletId << ".";
for (const auto& tx: l)
{
string hash = tx.hash;
ldebug << "Adding raw data to DB for tx " << hash << ".";
const auto result = db(select(blockchain_btc_raw_tx.raw_tx_id).from(blockchain_btc_raw_tx).where(blockchain_btc_raw_tx.hash == hash and blockchain_btc_raw_tx.blockchain_id == m_blockchainId));
if (!result.empty())
{
ldebug << "Raw data for tx " << hash << " is already in DB. Ignoring.";
db(insert_into(wallets_btc_raw_tx).set(
wallets_btc_raw_tx.raw_tx_id = result.front().raw_tx_id,
wallets_btc_raw_tx.wallet_id = walletId
));
}
else
{
db(insert_into(blockchain_btc_raw_tx).set(
blockchain_btc_raw_tx.hash = hash,
blockchain_btc_raw_tx.unix_time = tx.time.toUnixTime(),
blockchain_btc_raw_tx.blockchain_id = m_blockchainId
));
int rawTxId = db(select(max(blockchain_btc_raw_tx.raw_tx_id).as(max_raw_tx_id)).from(blockchain_btc_raw_tx).unconditionally()).front().max_raw_tx_id;
ldebug << "Raw tax id created : " << rawTxId << ".";
ldebug << "Adding inputs raw data to DB for tx " << hash << ".";
for (const auto& input : tx.inputs)
{
// Inputs are positive.
Money amount = input.second;
db(insert_into(blockchain_btc_raw_tx_details).set(
blockchain_btc_raw_tx_details.raw_tx_id = rawTxId,
blockchain_btc_raw_tx_details.amount = amount.toBoostMpf(),
blockchain_btc_raw_tx_details.address = input.first
));
}
ldebug << "Adding outputs raw data to DB for tx " << hash << ".";
for (const auto& output : tx.outputs)
{
// Outputs are negative.
Money amount = 0-output.second;
db(insert_into(blockchain_btc_raw_tx_details).set(
blockchain_btc_raw_tx_details.raw_tx_id = rawTxId,
blockchain_btc_raw_tx_details.amount = amount.toBoostMpf(),
blockchain_btc_raw_tx_details.address = output.first
));
}
db(insert_into(wallets_btc_raw_tx).set(
wallets_btc_raw_tx.raw_tx_id = rawTxId,
wallets_btc_raw_tx.wallet_id = walletId
));
}
}
}
list<BlockchainTxDetailsTypeBTC> WalletTypeBTC::getTxDetailsListFromRawDB(const int walletId)
{
mysql::connection db(getMysqlConfig());
const auto wallets_btc_raw_tx = TableWalletsBtcRawTx{};
const auto blockchain_btc_raw_tx = TableBlockchainBtcRawTx{};
const auto blockchain_btc_raw_tx_details = TableBlockchainBtcRawTxDetails{};
linfo << "Retrieving data from raw DB for " << getCoinName(m_coinId) << " wallet " << walletId << ".";
list<BlockchainTxDetailsTypeBTC> l;
for (const auto& rowTx: db.run(select(blockchain_btc_raw_tx.raw_tx_id,blockchain_btc_raw_tx.hash,blockchain_btc_raw_tx.unix_time).from(blockchain_btc_raw_tx.cross_join(wallets_btc_raw_tx)).where(blockchain_btc_raw_tx.raw_tx_id == wallets_btc_raw_tx.raw_tx_id and wallets_btc_raw_tx.wallet_id == walletId)))
{
ldebug << "Retrieving data from raw DB for tx " << rowTx.hash << ".";
BlockchainTxDetailsTypeBTC tx;
tx.hash = rowTx.hash;
tx.time.setFromUnixTime(rowTx.unix_time);
ldebug << "Retrieving inputs/outputs data from raw DB for tx " << rowTx.hash << ".";
for (const auto& row: db.run(select(blockchain_btc_raw_tx_details.amount,blockchain_btc_raw_tx_details.address).from(blockchain_btc_raw_tx_details).where(blockchain_btc_raw_tx_details.raw_tx_id == rowTx.raw_tx_id)))
{
Money m (row.amount);
if (m >= 0)
tx.inputs[row.address] = m;
else
tx.outputs[row.address] = 0-m;
}
l.push_back(tx);
}
return l;
}
void WalletTypeBTC::addTxDetailsListToDB(const int walletId, const list<string> walletAddresses, const list<BlockchainTxDetailsTypeBTC>& l)
{
mysql::connection db(getMysqlConfig());
const auto wallets_tx = TableWalletsTx{};
linfo << "Now adding data to DB for " << getCoinName(m_coinId) << " wallet " << walletId << ".";
for (const auto& tx: l)
{
Money txTotalOutputAmount;
Money txTotalInputAmount;
Money txWalletOutputAmount;
Money txWalletInputAmount;
bool bToWallet = false;
bool bFromWallet = false;
list<string> inputAddresses;
list<string> outputAddresses;
ltrace << "Now adding data to DB for tx " << tx.hash << ".";
for (const auto& input : tx.inputs)
{
//ltrace << "Input : " << input.first << ", amount : " << input.second;
txTotalInputAmount += input.second;
if (std::find(walletAddresses.begin(), walletAddresses.end(), input.first) != walletAddresses.end())
{
bFromWallet = true;
txWalletInputAmount += input.second;
}
else
{
inputAddresses.push_back(input.first);
}
}
for (const auto& output : tx.outputs)
{
//ltrace << "Output : " << output.first << ", amount : " << output.second;
txTotalOutputAmount += output.second;
if (std::find(walletAddresses.begin(), walletAddresses.end(), output.first) != walletAddresses.end())
{
bToWallet = true;
txWalletOutputAmount += output.second;
}
else
{
outputAddresses.push_back(output.first);
}
}
if (bFromWallet)
{
// In case at least one address was in the inputs, then ALL other inputs should be.
for (auto const& addr: inputAddresses)
{
if (std::find(walletAddresses.begin(), walletAddresses.end(), addr) == walletAddresses.end())
{
lwarn << "User configuration is missing this address: " << addr;
}
}
// In case at least one address was in the inputs, the outputs might be either another wallet for this user,
// or one of the wallet addresses might be missing from the configuration.
for (auto const& addr: outputAddresses)
{
if (std::find(walletAddresses.begin(), walletAddresses.end(), addr) == walletAddresses.end())
{
linfo << "Potential other address to analyze: " << L_Cyan << addr;
}
}
}
Money amount;
Money fee;
if (bFromWallet)
{
// The fee only applies in case this tx was initiated by the wallet
fee = txTotalInputAmount - txTotalOutputAmount;
amount = txWalletOutputAmount - txWalletInputAmount + fee;
}
else
{
amount = txWalletOutputAmount;
fee = 0;
}
db(insert_into(wallets_tx).set(
wallets_tx.wallet_id = walletId,
wallets_tx.amount = amount.toBoostMpf(),
wallets_tx.fee = fee.toBoostMpf(),
wallets_tx.unix_time = tx.time.toUnixTime(),
wallets_tx.amount_coin_id = m_coinId,
wallets_tx.fee_coin_id = m_coinId,
wallets_tx.operation_type = CPFM_WALLET_OPERATION_UNKNOWN
));
}
}
void WalletTypeBTC::updateBalanceFromTxDetailsInDB(int walletId)
{
mysql::connection db(getMysqlConfig());
const auto wallets_tx = TableWalletsTx{};
const auto wallets_balances = TableWalletsBalances{};
ldebug << "------------------------------------------------------------";
Money walletBalance = 0;
Money walletInputs = 0;
Money walletOutputs = 0;
Money walletFees = 0;
for (const auto& row: db.run(select(wallets_tx.amount, wallets_tx.fee).from(wallets_tx).where(wallets_tx.wallet_id == walletId).order_by(wallets_tx.unix_time.asc())))
{
Money txAmount(row.amount);
Money txFee(row.fee);
if (txAmount<0)
walletOutputs += txAmount;
else
walletInputs += txAmount;
walletFees += txFee;
walletBalance = walletInputs + walletOutputs - walletFees;
string sReason = "?";
ltrace << "Tx Amount: " << txAmount
<< ". Reason: " << sReason
<< ". Tx Fees: " << txFee
<< ". Total outputs: " << walletOutputs
<< ". Total inputs: " << walletInputs
<< ". Total Balance: " << walletBalance;
}
linfo << "Wallet " << walletId << " balance is: " << walletBalance;
db(remove_from(wallets_balances).where(wallets_balances.wallet_id == walletId));
db(insert_into(wallets_balances).set(
wallets_balances.wallet_id = walletId,
wallets_balances.coin_id = m_coinId,
wallets_balances.balance = walletBalance.toBoostMpf()
));
}

View File

@ -1,48 +0,0 @@
/*
* Copyright (c) 2021, 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/>.
*
*/
#ifndef CPFM_WALLET_TYPE_BTC_H_INCLUDED
#define CPFM_WALLET_TYPE_BTC_H_INCLUDED
#include "wallet.h"
#include "datasources/datasource.h"
class WalletTypeBTC : public Wallet
{
public:
WalletTypeBTC(int walletId):Wallet(walletId) {}
virtual void update();
private:
void addTxDetailsListToRawDB(const int walletId, const list<BlockchainTxDetailsTypeBTC>& l);
void addTxDetailsListToDB(const int walletId, const list<string> walletAddresses, const list<BlockchainTxDetailsTypeBTC>& l);
list<BlockchainTxDetailsTypeBTC> getTxDetailsListFromRawDB(const int walletId);
void updateBalanceFromTxDetailsInDB(int walletId);
protected:
virtual list<BlockchainTxDetailsTypeBTC> getTxDetailsListForAddresses(list<string> addresses) = 0;
int m_coinId;
int m_walletTypeId;
int m_blockchainId;
};
#endif // CPFM_WALLET_TYPE_BTC_H_INCLUDED

View File

@ -1,248 +0,0 @@
/*
* Copyright (c) 2021, 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/wallet_type_eth.h"
SQLPP_ALIAS_PROVIDER(max_raw_tx_id);
void WalletTypeETH::update()
{
if (!m_walletId)
{
lerr << "Wallet id not defined. Ignoring update.";
}
linfo << "Analyzing " << getBlockchainName(m_blockchainId) << " wallet " << m_walletId;
mysql::connection db(getMysqlConfig());
const auto wallets = TableWallets{};
const auto wallets_addresses = TableWalletsAddresses{};
const auto wallets_tx = TableWalletsTx{};
// Load wallet address
string address = db(select(wallets.wallet_id,wallets.type_id,wallets_addresses.address).from(wallets.cross_join(wallets_addresses)).where(wallets.wallet_id == wallets_addresses.wallet_id and wallets.wallet_id == m_walletId and wallets.type_id == m_walletTypeId)).front().address;
// Get wallet tx data from data source and insert it in the DB raw tables.
list<BlockchainTxDetailsTypeETH> l;
l = getTxDetailsListForAddress(address);
addTxDetailsListToRawDB(m_walletId,l);
// Load tx data from DB raw tables, analyze data, and insert it in DB tables.
l = getTxDetailsListFromRawDB(m_walletId);
db(remove_from(wallets_tx).where(wallets_tx.wallet_id == m_walletId));
addTxDetailsListToDB(m_walletId,address,l);
updateBalanceFromTxDetailsInDB(m_walletId);
}
void WalletTypeETH::addTxDetailsListToRawDB(const int walletId, const list<BlockchainTxDetailsTypeETH>& l)
{
mysql::connection db(getMysqlConfig());
const auto wallets_eth_raw_tx = TableWalletsEthRawTx{};
const auto blockchain_eth_raw_tx = TableBlockchainEthRawTx{};
const auto blockchain_eth_raw_tx_details = TableBlockchainEthRawTxDetails{};
linfo << "Clearing tx raw data links for " << getBlockchainName(m_blockchainId) << " wallet " << walletId << ".";
const auto result = db(remove_from(wallets_eth_raw_tx).where(wallets_eth_raw_tx.wallet_id == walletId));
linfo << "Now adding raw data to DB for " << getBlockchainName(m_blockchainId) << " wallet " << walletId << ".";
for (const auto& tx: l)
{
string hash = tx.hash;
ldebug << "Adding raw data to DB for tx " << hash << ".";
const auto result = db(select(blockchain_eth_raw_tx.raw_tx_id).from(blockchain_eth_raw_tx).where(blockchain_eth_raw_tx.hash == hash and blockchain_eth_raw_tx.blockchain_id == m_blockchainId));
if (!result.empty())
{
ldebug << "Raw data for tx " << hash << " is already in DB. Ignoring.";
db(insert_into(wallets_eth_raw_tx).set(
wallets_eth_raw_tx.raw_tx_id = result.front().raw_tx_id,
wallets_eth_raw_tx.wallet_id = walletId
));
}
else
{
Money fee = tx.fee;
db(insert_into(blockchain_eth_raw_tx).set(
blockchain_eth_raw_tx.hash = hash,
blockchain_eth_raw_tx.unix_time = tx.time.toUnixTime(),
blockchain_eth_raw_tx.blockchain_id = m_blockchainId,
blockchain_eth_raw_tx.fee = fee.toBoostMpf()
));
int rawTxId = db(select(max(blockchain_eth_raw_tx.raw_tx_id).as(max_raw_tx_id)).from(blockchain_eth_raw_tx).unconditionally()).front().max_raw_tx_id;
ldebug << "Raw tax id created : " << rawTxId << ".";
ldebug << "Adding raw data operations to DB for tx " << hash << ".";
for (const auto& op : tx.operations)
{
Money amount = op.amount;
db(insert_into(blockchain_eth_raw_tx_details).set(
blockchain_eth_raw_tx_details.raw_tx_id = rawTxId,
blockchain_eth_raw_tx_details.address_from = op.addressFrom,
blockchain_eth_raw_tx_details.address_to = op.addressTo,
blockchain_eth_raw_tx_details.amount = amount.toBoostMpf(),
blockchain_eth_raw_tx_details.amount_coin_id = op.amountCoinId
));
}
db(insert_into(wallets_eth_raw_tx).set(
wallets_eth_raw_tx.raw_tx_id = rawTxId,
wallets_eth_raw_tx.wallet_id = walletId
));
}
}
}
list<BlockchainTxDetailsTypeETH> WalletTypeETH::getTxDetailsListFromRawDB(const int walletId)
{
mysql::connection db(getMysqlConfig());
const auto wallets_eth_raw_tx = TableWalletsEthRawTx{};
const auto blockchain_eth_raw_tx = TableBlockchainEthRawTx{};
const auto blockchain_eth_raw_tx_details = TableBlockchainEthRawTxDetails{};
linfo << "Retrieving data from raw DB for " << getBlockchainName(m_blockchainId) << " wallet " << walletId << ".";
list<BlockchainTxDetailsTypeETH> l;
for (const auto& rowTx: db.run(select(blockchain_eth_raw_tx.raw_tx_id,blockchain_eth_raw_tx.hash,blockchain_eth_raw_tx.unix_time,blockchain_eth_raw_tx.fee).from(blockchain_eth_raw_tx.cross_join(wallets_eth_raw_tx)).where(blockchain_eth_raw_tx.raw_tx_id == wallets_eth_raw_tx.raw_tx_id and wallets_eth_raw_tx.wallet_id == walletId)))
{
ldebug << "Retrieving data from raw DB for tx " << rowTx.hash << ".";
BlockchainTxDetailsTypeETH tx;
tx.hash = rowTx.hash;
tx.time.setFromUnixTime(rowTx.unix_time);
tx.fee = rowTx.fee;
ldebug << "Retrieving operations data from raw DB for tx " << rowTx.hash << ".";
for (const auto& rowOp: db.run(select(blockchain_eth_raw_tx_details.amount,blockchain_eth_raw_tx_details.amount_coin_id,blockchain_eth_raw_tx_details.address_from,blockchain_eth_raw_tx_details.address_to).from(blockchain_eth_raw_tx_details).where(blockchain_eth_raw_tx_details.raw_tx_id == rowTx.raw_tx_id)))
{
BlockchainTxOperationTypeETH op;
op.addressFrom = rowOp.address_from;
op.addressTo = rowOp.address_to;
op.amount = rowOp.amount;
op.amountCoinId = rowOp.amount_coin_id;
tx.operations.push_back(op);
}
l.push_back(tx);
}
return l;
}
void WalletTypeETH::addTxDetailsListToDB(const int walletId, string address, const list<BlockchainTxDetailsTypeETH>& l)
{
mysql::connection db(getMysqlConfig());
const auto wallets_tx = TableWalletsTx{};
linfo << "Now adding data to DB for " << getBlockchainName(m_blockchainId) << " wallet " << walletId << ".";
for (const auto& tx: l)
{
ltrace << "Now adding data to DB for tx " << tx.hash << ".";
bool bFeeAdded = false;
for (const auto& op: tx.operations)
{
Money amount;
Money fee;
if (address == op.addressFrom)
{
// Even if we have multiple operations for the same tx, the fee only applies once.
if (!bFeeAdded)
{
// The fee only applies in case this tx was initiated by the wallet
fee = tx.fee;
}
amount = 0-op.amount;
}
else
{
amount = op.amount;
}
db(insert_into(wallets_tx).set(
wallets_tx.wallet_id = walletId,
wallets_tx.amount = amount.toBoostMpf(),
wallets_tx.fee = fee.toBoostMpf(),
wallets_tx.unix_time = tx.time.toUnixTime(),
wallets_tx.amount_coin_id = op.amountCoinId,
wallets_tx.fee_coin_id = m_coinId,
wallets_tx.operation_type = CPFM_WALLET_OPERATION_UNKNOWN
));
}
}
}
void WalletTypeETH::updateBalanceFromTxDetailsInDB(int walletId)
{
mysql::connection db(getMysqlConfig());
const auto wallets_tx = TableWalletsTx{};
const auto wallets_balances = TableWalletsBalances{};
ldebug << "------------------------------------------------------------";
map<int,Money> walletBalances;
for (const auto& row: db.run(select(wallets_tx.amount, wallets_tx.amount_coin_id, wallets_tx.fee).from(wallets_tx).where(wallets_tx.wallet_id == walletId).order_by(wallets_tx.unix_time.asc())))
{
Money txAmount(row.amount);
Money txFee(row.fee);
walletBalances[row.amount_coin_id] += txAmount;
walletBalances[m_coinId] -= txFee;
string sReason = "?";
ltrace << "Tx Amount: " << txAmount
<< ", Coin: " << getCoinName(row.amount_coin_id)
<< ". Reason: " << sReason
<< ". Tx Fees: " << txFee;
}
db(remove_from(wallets_balances).where(wallets_balances.wallet_id == walletId));
for (const auto b: walletBalances)
{
linfo << "Wallet " << walletId << " " << getCoinName(b.first) << " balance is: " << b.second;
Money m = b.second;
db(insert_into(wallets_balances).set(
wallets_balances.wallet_id = walletId,
wallets_balances.coin_id = b.first,
wallets_balances.balance = m.toBoostMpf()
));
}
}

View File

@ -1,48 +0,0 @@
/*
* Copyright (c) 2021, 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/>.
*
*/
#ifndef CPFM_WALLET_TYPE_ETH_H_INCLUDED
#define CPFM_WALLET_TYPE_ETH_H_INCLUDED
#include "wallet.h"
#include "datasources/datasource.h"
class WalletTypeETH : public Wallet
{
public:
WalletTypeETH(int walletId):Wallet(walletId) {}
virtual void update();
private:
void addTxDetailsListToRawDB(const int walletId, const list<BlockchainTxDetailsTypeETH>& l);
void addTxDetailsListToDB(const int walletId, string address, const list<BlockchainTxDetailsTypeETH>& l);
list<BlockchainTxDetailsTypeETH> getTxDetailsListFromRawDB(const int walletId);
void updateBalanceFromTxDetailsInDB(int walletId);
protected:
virtual list<BlockchainTxDetailsTypeETH> getTxDetailsListForAddress(string address) = 0;
int m_walletTypeId;
int m_blockchainId;
int m_coinId;
};
#endif // CPFM_WALLET_TYPE_ETH_H_INCLUDED