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.
 
 
 

248 lines
9.5 KiB

/*
* 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()
));
}
}