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

371 lines
10 KiB

/*
* Copyright (c) 2018, evilny0
* Copyright (c) 2013 - Marco E. <marco.esposito@gmail.com>
*
* 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 "kraken.h"
ExchangeHandlerKraken::ExchangeHandlerKraken(int userId, int accountId) : ExchangeHandler(userId, accountId)
{
getAccountDataFromDB();
}
ExchangeHandlerKraken::~ExchangeHandlerKraken()
{
}
void ExchangeHandlerKraken::getAccountDataFromDB()
{
mysql::connection db(getMysqlConfig());
const auto exchanges_accounts = TableExchangesAccounts{};
for (const auto& row: db.run(select(all_of(exchanges_accounts)).from(exchanges_accounts).where(exchanges_accounts.account_id == m_accountId)))
{
m_apiKey = row.api_key;
m_apiPrivateKey = row.api_private_key;
}
}
void ExchangeHandlerKraken::analyzeUserData()
{
getAccountBalance();
analyzeAccountBalance();
//getAccountTrades();
//analyzeAccountTrades();
//getAccountLedgers();
//analyzeAccountLedgers();
}
void ExchangeHandlerKraken::analyzeAccountBalance()
{
if (!bfs::exists(getCacheFilename("balance")))
{
lerr << "Cached balance file not found.";
return;
}
ifstream f;
f.open(getCacheFilename("balance"));
json::value jvalue = json::value::parse(f);
f.close();
analyzeAccountBalanceJSON(jvalue);
}
void ExchangeHandlerKraken::analyzeAccountBalanceJSON(json::value jvalue)
{
if (jvalue.is_null())
{
lerr << "Kraken balance answer is null.";
return;
}
if (jvalue["error"].size())
{
lerr << "Error in Kraken balance answer: " << jvalue["error"][0].as_string();
return;
}
mysql::connection db(getMysqlConfig());
const auto exchanges_balances = TableExchangesBalances{};
for(auto it = jvalue["result"].as_object().cbegin(); it != jvalue["result"].as_object().cend(); ++it)
{
const json::value &v = it->second;
string coinName = it->first;
Money m(v.as_string());
int coinId = 0;
if (!coinName.compare("XXBT"))
coinId = CPFM_COIN_ID_BTC;
else if (!coinName.compare("XLTC"))
coinId = CPFM_COIN_ID_LTC;
else if (!coinName.compare("ZEUR"))
coinId = CPFM_COIN_ID_EUR;
else if (!coinName.compare("XICN"))
coinId = CPFM_COIN_ID_ICN;
else if (!coinName.compare("XETC"))
coinId = CPFM_COIN_ID_ETC;
else if (!coinName.compare("XETH"))
coinId = CPFM_COIN_ID_ETH;
else if (!coinName.compare("BCH"))
coinId = CPFM_COIN_ID_BCH;
if (!coinId)
{
lerr << "Unknown coin [" << coinName << "]. Ignoring balance for this coin.";
}
else
{
db(insert_into(exchanges_balances).set(
exchanges_balances.account_id = m_accountId,
exchanges_balances.coin_id = coinId,
exchanges_balances.balance = m.toBoostMpf()
));
}
}
}
__int64 ExchangeHandlerKraken::getNonce()
{
return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}
string ExchangeHandlerKraken::getCacheFilename(string operation)
{
stringstream ss;
ss << "data/cache/kraken/" << m_userId << "-" << operation;
return ss.str();
}
void ExchangeHandlerKraken::getAccountBalance()
{
linfo << "Getting user balance.";
if (!bfs::exists(getCacheFilename("balance")))
{
getAccountBalanceFromAPI();
}
}
void ExchangeHandlerKraken::getAccountLedgers()
{
linfo << "Getting user ledgers.";
if (!bfs::exists(getCacheFilename("ledgers")))
{
getAccountLedgersFromAPI();
}
}
void ExchangeHandlerKraken::getAccountTrades()
{
linfo << "Getting user trades.";
if (!bfs::exists(getCacheFilename("trades")))
{
getAccountTradesFromAPI();
}
}
vector<unsigned char> ExchangeHandlerKraken::sha256(const string& data)
{
vector<unsigned char> digest(SHA256_DIGEST_LENGTH);
SHA256_CTX ctx;
SHA256_Init(&ctx);
SHA256_Update(&ctx, data.c_str(), data.length());
SHA256_Final(digest.data(), &ctx);
return digest;
}
vector<unsigned char> ExchangeHandlerKraken::b64_decode(const string& data)
{
BIO* b64 = BIO_new(BIO_f_base64());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
BIO* bmem = BIO_new_mem_buf((void*)data.c_str(),data.length());
bmem = BIO_push(b64, bmem);
std::vector<unsigned char> output(data.length());
int decoded_size = BIO_read(bmem, output.data(), output.size());
BIO_free_all(bmem);
if (decoded_size < 0)
throw std::runtime_error("failed while decoding base64.");
return output;
}
string ExchangeHandlerKraken::b64_encode(const vector<unsigned char>& data)
{
BIO* b64 = BIO_new(BIO_f_base64());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
BIO* bmem = BIO_new(BIO_s_mem());
b64 = BIO_push(b64, bmem);
BIO_write(b64, data.data(), data.size());
BIO_flush(b64);
BUF_MEM* bptr = NULL;
BIO_get_mem_ptr(b64, &bptr);
std::string output(bptr->data, bptr->length);
BIO_free_all(b64);
return output;
}
vector<unsigned char> ExchangeHandlerKraken::hmac_sha512(const vector<unsigned char>& data, const vector<unsigned char>& key)
{
unsigned int len = EVP_MAX_MD_SIZE;
vector<unsigned char> digest(len);
HMAC_CTX* ctx = HMAC_CTX_new();
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);
return digest;
}
string ExchangeHandlerKraken::getSignature(const string& sRequestURL, const __int64& nonce, const string& sBody)
{
vector<unsigned char> data(sRequestURL.begin(), sRequestURL.end());
stringstream ss;
ss << nonce;
string sNonce = ss.str();
vector<unsigned char> nonce_postdata = sha256(sNonce + sBody);
data.insert(data.end(), nonce_postdata.begin(), nonce_postdata.end());
return b64_encode( hmac_sha512(data, b64_decode(m_apiPrivateKey)) );
}
void ExchangeHandlerKraken::getAccountBalanceFromAPI()
{
try
{
string sRequestURL = "/0/private/Balance";
http_client apiclient(KRAKEN_API_URL);
__int64 nonce = getNonce();
stringstream ss;
ss << "nonce=" << nonce;
string signature = getSignature(sRequestURL,nonce,ss.str());
http_request request(methods::POST);
request.headers().add("API-Key", m_apiKey);
request.headers().add("API-Sign", signature);
request.set_request_uri(sRequestURL);
request.set_body( ss.str(), "application/x-www-form-urlencoded" );
apiclient.request(request).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("balance"));
f << previousTask.get();
f.close();
})
.wait();
}
catch(const http::http_exception& e)
{
lerr << "Failed to query Kraken account balance";
}
}
void ExchangeHandlerKraken::getAccountLedgersFromAPI()
{
try
{
string sRequestURL = "/0/private/Ledgers";
http_client apiclient(KRAKEN_API_URL);
__int64 nonce = getNonce();
stringstream ss;
ss << "nonce=" << nonce;
string signature = getSignature(sRequestURL,nonce,ss.str());
http_request request(methods::POST);
request.headers().add("API-Key", m_apiKey);
request.headers().add("API-Sign", signature);
request.set_request_uri(sRequestURL);
request.set_body( ss.str(), "application/x-www-form-urlencoded" );
apiclient.request(request).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("ledgers"));
f << previousTask.get();
f.close();
})
.wait();
}
catch(const http::http_exception& e)
{
lerr << "Failed to query Kraken account balance";
}
}
void ExchangeHandlerKraken::getAccountTradesFromAPI()
{
try
{
string sRequestURL = "/0/private/TradesHistory";
http_client apiclient(KRAKEN_API_URL);
__int64 nonce = getNonce();
stringstream ss;
ss << "nonce=" << nonce;
string signature = getSignature(sRequestURL,nonce,ss.str());
http_request request(methods::POST);
request.headers().add("API-Key", m_apiKey);
request.headers().add("API-Sign", signature);
request.set_request_uri(sRequestURL);
request.set_body( ss.str(), "application/x-www-form-urlencoded" );
apiclient.request(request).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("trades"));
f << previousTask.get();
f.close();
})
.wait();
}
catch(const http::http_exception& e)
{
lerr << "Failed to query Kraken account balance";
}
}