Skip to content

Commit

Permalink
Merge pull request #167 from DeFiCh/fix/auto_select_balances
Browse files Browse the repository at this point in the history
Fix auto selecting balances
  • Loading branch information
monstrobishi authored Jan 13, 2021
2 parents a3e61f9 + db2c0b6 commit 6a55d99
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 22 deletions.
10 changes: 4 additions & 6 deletions src/masternodes/balances.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ struct CBalances
return Res::Ok();
}

CAmount GetAllTokensAmount() {
CAmount GetAllTokensAmount() const {
CAmount sum = 0;
for (auto& balance : balances) {
sum += balance.second;
Expand Down Expand Up @@ -125,12 +125,10 @@ struct CBalances
friend bool operator<(const CBalances& a, const CBalances& b) {
for (const auto& a_kv : a.balances) {
const auto b_value_it = b.balances.find(a_kv.first);
CAmount b_value = 0;
if (b_value_it != b.balances.end()) {
b_value = b_value_it->second;
}
if (a_kv.second >= b_value) {
return false;
if (a_kv.second >= b_value_it->second) {
return false;
}
}
}
return true;
Expand Down
36 changes: 20 additions & 16 deletions src/masternodes/mn_rpc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,57 +110,61 @@ static AccountSelectionMode ParseAccountSelectionParam(const std::string selecti
}

static CAccounts SelectAccountsByTargetBalances(const CAccounts& accounts, const CBalances& targetBalances, AccountSelectionMode selectionMode) {
CAccounts selectedAccountsBalances;

std::vector<std::pair<CScript, CBalances>> foundAccountsBalances;
CBalances residualBalances(targetBalances);
// iterate at all accounts to finding all accounts with neccessaru token balances
for (auto accIt = accounts.begin(); accIt != accounts.end(); accIt++) {
for (const auto& account : accounts) {
// selectedBalances accumulates overlap between account balances and residual balances
CBalances selectedBalances;
// iterate at residual balances to find neccessary tokens in account
for (auto balIt = residualBalances.balances.begin(); balIt != residualBalances.balances.end(); balIt++) {
for (const auto& balance : targetBalances.balances) {
// find neccessary token amount from current account
auto foundTokenAmount = accIt->second.balances.find(balIt->first);
const auto& accountBalances = account.second.balances;
auto foundTokenAmount = accountBalances.find(balance.first);
// account balance has neccessary token
if (foundTokenAmount != accIt->second.balances.end()) {
if (foundTokenAmount != accountBalances.end()) {
// add token amount to selected balances from current account
selectedBalances.Add(CTokenAmount{foundTokenAmount->first, foundTokenAmount->second});
}
}
if (!selectedBalances.balances.empty()) {
// added account and selected balances from account to selected accounts
foundAccountsBalances.emplace_back(accIt->first, selectedBalances);
foundAccountsBalances.emplace_back(account.first, selectedBalances);
}
}

if (selectionMode != SelectionForward) {
// we need sort vector by ascending or descending sum of token amounts
std::sort(foundAccountsBalances.begin(), foundAccountsBalances.end(), [&](std::pair<CScript, CBalances> p1, std::pair<CScript, CBalances> p2) {
std::sort(foundAccountsBalances.begin(), foundAccountsBalances.end(), [&](const std::pair<CScript, CBalances>& p1, const std::pair<CScript, CBalances>& p2) {
return (selectionMode == SelectionCrumbs) ?
p1.second.GetAllTokensAmount() > p2.second.GetAllTokensAmount() :
p1.second.GetAllTokensAmount() < p2.second.GetAllTokensAmount();
p1.second.GetAllTokensAmount() < p2.second.GetAllTokensAmount();
});
}

CAccounts selectedAccountsBalances;
CBalances residualBalances(targetBalances);
// selecting accounts balances
for (auto accIt = foundAccountsBalances.begin(); accIt != foundAccountsBalances.end(); accIt++) {
for (const auto& accountBalances : foundAccountsBalances) {
// Substract residualBalances and selectedBalances with remainder.
// Substraction with remainder will remove tokenAmount from balances if remainder
// of token's amount is not zero (we got negative result of substraction)
CBalances remainder = residualBalances.SubBalancesWithRemainder(accIt->second.balances);
CBalances finalBalances(accountBalances.second);
CBalances remainder = residualBalances.SubBalancesWithRemainder(finalBalances.balances);
// calculate final balances by substraction account balances with remainder
// it is necessary to get rid of excess
CBalances finalBalances(accIt->second);
finalBalances.SubBalances(remainder.balances);
if (!finalBalances.balances.empty())
selectedAccountsBalances.emplace(accIt->first, finalBalances);
if (!finalBalances.balances.empty()) {
selectedAccountsBalances.emplace(accountBalances.first, finalBalances);
}
// if residual balances is empty we found all neccessary token amounts and can stop selecting
if (residualBalances.balances.empty())
if (residualBalances.balances.empty()) {
break;
}
}

const auto selectedBalancesSum = SumAllTransfers(selectedAccountsBalances);
if (selectedBalancesSum != targetBalances) {
if (selectedBalancesSum < targetBalances) {
// we have not enough tokens balance to transfer
return {};
}
Expand Down

0 comments on commit 6a55d99

Please sign in to comment.