/* * Copyright (c) 2018, evilny0 * Copyright (c) 2013 - Marco E. * * This file is part of cpfm. * * cpfm is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * cpm is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with cpfm. If not, see . */ #include "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::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 ExchangeHandlerKraken::sha256(const string& data) { vector 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 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 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& 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 ExchangeHandlerKraken::hmac_sha512(const vector& data, const vector& key) { unsigned int len = EVP_MAX_MD_SIZE; vector digest(len); 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_CTX_cleanup(&ctx); return digest; } string ExchangeHandlerKraken::getSignature(const string& sRequestURL, const __int64& nonce, const string& sBody) { vector data(sRequestURL.begin(), sRequestURL.end()); stringstream ss; ss << nonce; string sNonce = ss.str(); vector 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 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 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 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"; } }