diff --git a/src/masternodes/poolpairs.cpp b/src/masternodes/poolpairs.cpp index 56365619fd..6aea773881 100644 --- a/src/masternodes/poolpairs.cpp +++ b/src/masternodes/poolpairs.cpp @@ -2,15 +2,12 @@ // Distributed under the MIT software license, see the accompanying // file LICENSE or http://www.opensource.org/licenses/mit-license.php. -#include - #include +#include #include #include #include -#include - struct PoolSwapValue { bool swapEvent; CAmount blockCommissionA; @@ -204,50 +201,28 @@ inline CAmount liquidityReward(CAmount reward, CAmount liquidity, CAmount totalL return static_cast((arith_uint256(reward) * arith_uint256(liquidity) / arith_uint256(totalLiquidity)).GetLow64()); } -template -bool MatchPoolId(TIterator & it, DCT_ID poolId) { - return it.Valid() && it.Key().poolID == poolId; -} - template void ReadValueMoveToNext(TIterator & it, DCT_ID poolId, ValueType & value, uint32_t & height) { - if (MatchPoolId(it, poolId)) { + if (it.Valid() && it.Key().poolID == poolId) { value = it.Value(); /// @Note we store keys in desc order so Prev is actually go in forward it.Prev(); - height = MatchPoolId(it, poolId) ? it.Key().height : UINT_MAX; + if (it.Valid() && it.Key().poolID == poolId) { + height = it.Key().height; + } else { + height = UINT_MAX; + } } else { + value = {}; height = UINT_MAX; } } -template -auto InitPoolVars(CPoolPairView & view, PoolHeightKey poolKey, uint32_t end) { - - auto poolId = poolKey.poolID; - auto it = view.LowerBound(poolKey); - - auto height = poolKey.height; - static const uint32_t startHeight = Params().GetConsensus().FortCanningSpringHeight; - poolKey.height = std::max(height, startHeight); - - while (!MatchPoolId(it, poolId) && poolKey.height < end) { - height = poolKey.height; - it.Seek(poolKey); - poolKey.height++; - } - - Value value = MatchPoolId(it, poolId) ? it.Value() : Value{}; - - return std::make_tuple(std::move(value), std::move(it), height); -} - void CPoolPairView::CalculatePoolRewards(DCT_ID const & poolId, std::function onLiquidity, uint32_t begin, uint32_t end, std::function onReward) { if (begin >= end) { return; } - constexpr const uint32_t PRECISION = 10000; const auto newCalcHeight = uint32_t(Params().GetConsensus().BayfrontGardensHeight); @@ -256,19 +231,28 @@ void CPoolPairView::CalculatePoolRewards(DCT_ID const & poolId, std::function(*this, poolKey, end); - auto nextPoolReward = startPoolReward; - - auto [poolLoanReward, itPoolLoanReward, startPoolLoanReward] = InitPoolVars(*this, poolKey, end); - auto nextPoolLoanReward = startPoolLoanReward; + CAmount poolReward = 0; + CAmount poolLoanReward = 0; + auto nextPoolReward = begin; + auto nextPoolLoanReward = begin; + auto itPoolReward = LowerBound(poolKey); + auto itPoolLoanReward = LowerBound(poolKey); - auto [totalLiquidity, itTotalLiquidity, nextTotalLiquidity] = InitPoolVars(*this, poolKey, end); + CAmount totalLiquidity = 0; + auto nextTotalLiquidity = begin; + auto itTotalLiquidity = LowerBound(poolKey); - auto [customRewards, itCustomRewards, startCustomRewards] = InitPoolVars(*this, poolKey, end); - auto nextCustomRewards = startCustomRewards; + CBalances customRewards; + auto nextCustomRewards = begin; + auto itCustomRewards = LowerBound(poolKey); - auto [poolSwap, itPoolSwap, poolSwapHeight] = InitPoolVars(*this, poolKey, end); - auto nextPoolSwap = poolSwapHeight; + PoolSwapValue poolSwap{}; + auto nextPoolSwap = UINT_MAX; + auto poolSwapHeight = UINT_MAX; + auto itPoolSwap = LowerBound(poolKey); + if (itPoolSwap.Valid() && itPoolSwap.Key().poolID == poolId) { + nextPoolSwap = itPoolSwap.Key().height; + } for (auto height = begin; height < end;) { // find suitable pool liquidity @@ -293,7 +277,7 @@ void CPoolPairView::CalculatePoolRewards(DCT_ID const & poolId, std::function= startPoolReward && poolReward != 0) { + if (poolReward != 0) { CAmount providerReward = 0; if (height < newCalcHeight) { // old calculation uint32_t liqWeight = liquidity * PRECISION / totalLiquidity; @@ -303,7 +287,7 @@ void CPoolPairView::CalculatePoolRewards(DCT_ID const & poolId, std::function= startPoolLoanReward && poolLoanReward != 0) { + if (poolLoanReward != 0) { CAmount providerReward = liquidityReward(poolLoanReward, liquidity, totalLiquidity); onReward(RewardType::LoanTokenDEXReward, {DCT_ID{0}, providerReward}, height); } @@ -326,11 +310,9 @@ void CPoolPairView::CalculatePoolRewards(DCT_ID const & poolId, std::function= startCustomRewards) { - for (const auto& reward : customRewards.balances) { - if (auto providerReward = liquidityReward(reward.second, liquidity, totalLiquidity)) { - onReward(RewardType::Pool, {reward.first, providerReward}, height); - } + for (const auto& reward : customRewards.balances) { + if (auto providerReward = liquidityReward(reward.second, liquidity, totalLiquidity)) { + onReward(RewardType::Pool, {reward.first, providerReward}, height); } } ++height; diff --git a/src/masternodes/rpc_accounts.cpp b/src/masternodes/rpc_accounts.cpp index ac527022e5..6cfec584c1 100644 --- a/src/masternodes/rpc_accounts.cpp +++ b/src/masternodes/rpc_accounts.cpp @@ -2399,19 +2399,22 @@ UniValue logaccountbalances(const JSONRPCRequest& request) { if (p.size() > 0) { outToLog = p[0].get_bool(); } if (p.size() > 1) { outToRpc = p[1].get_bool(); } + LOCK(cs_main); + std::map> accounts; - auto iter = pcustomcsDB->NewIterator(); - auto n = IterateKV([&](BalanceKey key, CAmount val) { - auto owner = ScriptToString(key.owner); + size_t count{}; + pcustomcsview->ForEachBalance([&](CScript const & owner, CTokenAmount balance) { + ++count; + auto ownerStr = ScriptToString(owner); if (outToLog) - LogPrintf("AccountBalance: (%s: %d@%d)\n", owner, val, key.tokenID.v); + LogPrintf("AccountBalance: (%s: %d@%d)\n", ownerStr, balance.nValue, balance.nTokenId.v); if (outToRpc) - accounts[owner].push_back(CTokenAmount{{key.tokenID.v}, val}); + accounts[ownerStr].push_back(CTokenAmount{{balance.nTokenId.v}, balance.nValue}); return true; - }, BalanceKey{}, std::move(iter)); + }); if (outToLog) - LogPrintf("IndexStats: (balances: %d)\n", n); + LogPrintf("IndexStats: (balances: %d)\n", count); if (!outToRpc) return {}; @@ -2427,7 +2430,7 @@ UniValue logaccountbalances(const JSONRPCRequest& request) { } result.pushKV("accounts", accountsJson); - result.pushKV("count", static_cast(n)); + result.pushKV("count", static_cast(count)); return result; } diff --git a/test/functional/feature_commission_fix.py b/test/functional/feature_commission_fix.py deleted file mode 100755 index 6878dda78a..0000000000 --- a/test/functional/feature_commission_fix.py +++ /dev/null @@ -1,304 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (c) 2014-2019 The Bitcoin Core developers -# Copyright (c) DeFi Blockchain Developers -# Distributed under the MIT software license, see the accompanying -# file LICENSE or http://www.opensource.org/licenses/mit-license.php. -"""Test commission fix""" - -from test_framework.test_framework import DefiTestFramework - -from test_framework.util import assert_equal -from decimal import Decimal -import time - -class CommissionFixTest(DefiTestFramework): - def set_test_params(self): - self.num_nodes = 1 - self.setup_clean_chain = True - self.fortcanningspring = 200 - self.extra_args = [ - ['-txnotokens=0', '-amkheight=1', '-bayfrontheight=1', '-eunosheight=1', '-fortcanningheight=1', '-fortcanningmuseumheight=1', '-fortcanninghillheight=1', '-fortcanningroadheight=1', '-fortcanningcrunchheight=1', f'-fortcanningspringheight={self.fortcanningspring}', '-subsidytest=1']] - - def run_test(self): - # Set up test tokens - self.setup_test_tokens() - - # Set up pool - self.setup_test_pool() - - # Test pool commission - self.pool_commission() - - # Set up pool after fork - self.setup_test_pool_fork() - - # Test pool commission after fork - self.pool_commission_fork() - - def setup_test_tokens(self): - # Generate chain - self.nodes[0].generate(101) - - # Symbols - self.symbolDUSD = 'DUSD' - self.symbolGOOGL = 'GOOGL' - self.symbolTSLA = 'TSLA' - self.symbolGD = 'GOOGL-DUSD' - self.symbolTD = 'TSLA-DUSD' - - # Store address - self.address = self.nodes[0].get_genesis_keys().ownerAuthAddress - - # Price feeds - price_feed = [ - {"currency": "USD", "token": self.symbolDUSD}, - {"currency": "USD", "token": self.symbolTSLA}, - ] - - # Appoint oracle - oracle_address = self.nodes[0].getnewaddress("", "legacy") - self.oracle = self.nodes[0].appointoracle(oracle_address, price_feed, 10) - self.nodes[0].generate(1) - - # Set Oracle prices - oracle_prices = [ - {"currency": "USD", "tokenAmount": f"1@{self.symbolDUSD}"}, - {"currency": "USD", "tokenAmount": f"1@{self.symbolTSLA}"}, - ] - self.nodes[0].setoracledata(self.oracle, int(time.time()), oracle_prices) - self.nodes[0].generate(10) - - # Create tokens - self.nodes[0].createtoken({ - 'symbol': self.symbolGOOGL, - 'name': self.symbolGOOGL, - "isDAT": True, - "collateralAddress": self.address - }) - self.nodes[0].generate(1) - - self.nodes[0].createtoken({ - 'symbol': self.symbolDUSD, - 'name': self.symbolDUSD, - "isDAT": True, - "collateralAddress": self.address - }) - self.nodes[0].generate(1) - - self.nodes[0].setloantoken({ - 'symbol': self.symbolTSLA, - 'name': self.symbolTSLA, - 'fixedIntervalPriceId': f"{self.symbolTSLA}/USD", - "isDAT": True, - 'interest': 0 - }) - self.nodes[0].generate(1) - - # Store token IDs - self.idDUSD = list(self.nodes[0].gettoken(self.symbolDUSD).keys())[0] - self.idGOOGL = list(self.nodes[0].gettoken(self.symbolGOOGL).keys())[0] - self.idTSLA = list(self.nodes[0].gettoken(self.symbolTSLA).keys())[0] - - # Mint some loan tokens - self.nodes[0].minttokens([ - f'1000000@{self.symbolDUSD}', - f'1000000@{self.symbolGOOGL}', - f'1000000@{self.symbolTSLA}', - ]) - self.nodes[0].generate(1) - - def setup_test_pool(self): - - # Create pool pair - self.nodes[0].createpoolpair({ - "tokenA": self.symbolGOOGL, - "tokenB": self.symbolDUSD, - "commission": 0.01, - "status": True, - "ownerAddress": self.address, - "symbol": self.symbolGD - }) - self.nodes[0].generate(1) - - # Store pool ID - self.idGD = list(self.nodes[0].gettoken(self.symbolGD).keys())[0] - - def setup_test_pool_fork(self): - - # Create pool pair - self.nodes[0].createpoolpair({ - "tokenA": self.symbolTSLA, - "tokenB": self.symbolDUSD, - "commission": 0.01, - "status": True, - "ownerAddress": self.address, - "symbol": self.symbolTD - }) - self.nodes[0].generate(1) - - # Store pool ID - self.idTD = list(self.nodes[0].gettoken(self.symbolTD).keys())[0] - - def pool_commission(self): - - # Set up commission address - commission_address = self.nodes[0].getnewaddress("", "legacy") - self.nodes[0].sendtoaddress(commission_address, 1) - self.nodes[0].accounttoaccount(self.address, { - commission_address: [f'1000@{self.symbolGOOGL}', f'1000@{self.symbolDUSD}'] - }) - self.nodes[0].generate(1) - - # Save block for revert - revert_block = self.nodes[0].getblockcount() + 1 - - # Add pool liquidity - self.nodes[0].addpoolliquidity({ - commission_address: [f'100@{self.symbolGOOGL}', f'100@{self.symbolDUSD}'] - }, commission_address) - self.nodes[0].generate(1) - - # Add liquidity twice for valid GetShare, possible bug? - self.nodes[0].addpoolliquidity({ - commission_address: [f'100@{self.symbolGOOGL}', f'100@{self.symbolDUSD}'] - }, commission_address) - self.nodes[0].generate(1) - - # Execute pool swap - self.nodes[0].poolswap({ - "from": self.address, - "tokenFrom": self.symbolGOOGL, - "amountFrom": 1, - "to": self.address, - "tokenTo": self.symbolDUSD - }) - self.nodes[0].generate(2) - - # Commission missing - for result in self.nodes[0].listaccounthistory(commission_address): - assert(result['type'] != 'Commission') - - # Show Commission with low depth - result = self.nodes[0].listaccounthistory(commission_address, {'depth': 1}) - assert_equal(result[0]['type'], 'Commission') - - # Test accounttoaccount temp fix - self.nodes[0].accounttoaccount(commission_address, { - commission_address: self.nodes[0].getaccount(commission_address) - }) - self.nodes[0].generate(1) - - # Execute pool swap - self.nodes[0].poolswap({ - "from": self.address, - "tokenFrom": self.symbolGOOGL, - "amountFrom": 1, - "to": self.address, - "tokenTo": self.symbolDUSD - }) - self.nodes[0].generate(2) - - # Show Commission - result = self.nodes[0].listaccounthistory(commission_address) - assert_equal(result[0]['type'], 'Commission') - - # Revert to before add liqudity - self.nodes[0].invalidateblock(self.nodes[0].getblockhash(revert_block)) - self.nodes[0].clearmempool() - - # Check pool empty - result = self.nodes[0].getpoolpair(self.symbolGD) - assert_equal(result[f'{self.idGD}']['reserveA'], Decimal('0')) - - # Move to fork - self.nodes[0].generate(self.fortcanningspring - self.nodes[0].getblockcount()) - - # Add pool liquidity - self.nodes[0].addpoolliquidity({ - commission_address: [f'100@{self.symbolGOOGL}', f'100@{self.symbolDUSD}'] - }, commission_address) - self.nodes[0].generate(1) - - # Add liquidity twice for valid GetShare, possible bug? - self.nodes[0].addpoolliquidity({ - commission_address: [f'100@{self.symbolGOOGL}', f'100@{self.symbolDUSD}'] - }, commission_address) - self.nodes[0].generate(1) - - # Execute pool swap - self.nodes[0].poolswap({ - "from": self.address, - "tokenFrom": self.symbolGOOGL, - "amountFrom": 1, - "to": self.address, - "tokenTo": self.symbolDUSD - }) - self.nodes[0].generate(2) - - # Show Commission - result = self.nodes[0].listaccounthistory(commission_address) - assert_equal(result[0]['type'], 'Commission') - - def pool_commission_fork(self): - - # Set up commission address - commission_address = self.nodes[0].getnewaddress("", "legacy") - self.nodes[0].sendtoaddress(commission_address, 1) - self.nodes[0].accounttoaccount(self.address, { - commission_address: [f'1000@{self.symbolTSLA}', f'1000@{self.symbolDUSD}'] - }) - self.nodes[0].generate(1) - - # Add pool liquidity - self.nodes[0].addpoolliquidity({ - commission_address: [f'100@{self.symbolTSLA}', f'100@{self.symbolDUSD}'] - }, commission_address) - self.nodes[0].generate(1) - - # Add liquidity twice for valid GetShare, possible bug? - self.nodes[0].addpoolliquidity({ - commission_address: [f'100@{self.symbolTSLA}', f'100@{self.symbolDUSD}'] - }, commission_address) - self.nodes[0].generate(1) - - # Execute pool swap - self.nodes[0].poolswap({ - "from": self.address, - "tokenFrom": self.symbolTSLA, - "amountFrom": 1, - "to": self.address, - "tokenTo": self.symbolDUSD - }) - self.nodes[0].generate(2) - - # Show Commission - result = self.nodes[0].listaccounthistory(commission_address) - assert_equal(result[0]['type'], 'Commission') - - # Token split - self.nodes[0].setgov({"ATTRIBUTES":{f'v0/oracles/splits/{str(self.nodes[0].getblockcount() + 2)}':f'{self.idTSLA}/2'}}) - self.nodes[0].generate(2) - - # Swap old for new values - self.idTSLA = list(self.nodes[0].gettoken(self.symbolTSLA).keys())[0] - - # Unlock token - self.nodes[0].setgov({"ATTRIBUTES":{f'v0/locks/token/{self.idTSLA}':'false'}}) - self.nodes[0].generate(1) - - # Execute pool swap - self.nodes[0].poolswap({ - "from": self.address, - "tokenFrom": self.symbolTSLA, - "amountFrom": 1, - "to": self.address, - "tokenTo": self.symbolDUSD - }) - self.nodes[0].generate(2) - - # Show Commission - result = self.nodes[0].listaccounthistory(commission_address) - assert_equal(result[0]['type'], 'Commission') - -if __name__ == '__main__': - CommissionFixTest().main() diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index b3a77d8ecd..a1332097ce 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -182,7 +182,6 @@ 'feature_poolswap_mechanism.py', 'feature_poolswap_mainnet.py', 'feature_prevent_bad_tx_propagation.py', - 'feature_commission_fix.py', 'feature_masternode_operator.py', 'feature_mine_cached.py', 'feature_mempool_dakota.py',