From 46ef729e65b44cd44a77dbf2e2e3444555da0535 Mon Sep 17 00:00:00 2001 From: Anthony Fieroni Date: Wed, 27 Apr 2022 09:59:08 +0300 Subject: [PATCH 01/16] Live dex statistics (#1192) Signed-off-by: Anthony Fieroni --- src/masternodes/govvariables/attributes.cpp | 19 ++++++-- src/masternodes/govvariables/attributes.h | 53 +++++++++++++++++++-- src/masternodes/gv.cpp | 24 +++++++++- src/masternodes/mn_checks.cpp | 39 +++++++++++++-- src/validation.cpp | 10 ++-- test/functional/feature_poolswap.py | 43 +++++++++++++++-- 6 files changed, 170 insertions(+), 18 deletions(-) diff --git a/src/masternodes/govvariables/attributes.cpp b/src/masternodes/govvariables/attributes.cpp index cbf62b8910..d809a13839 100644 --- a/src/masternodes/govvariables/attributes.cpp +++ b/src/masternodes/govvariables/attributes.cpp @@ -152,6 +152,7 @@ const std::map>& ATTRIBUTES::displayKeys {EconomyKeys::DFIP2203Current, "dfip2203_current"}, {EconomyKeys::DFIP2203Burned, "dfip2203_burned"}, {EconomyKeys::DFIP2203Minted, "dfip2203_minted"}, + {EconomyKeys::DexTokens, "dex"}, } }, }; @@ -425,7 +426,7 @@ Res ATTRIBUTES::RefundFuturesContracts(CCustomCSView &mnview, const uint32_t hei } } - attributes[liveKey] = balances; + SetValue(liveKey, std::move(balances)); return Res::Ok(); } @@ -453,11 +454,11 @@ Res ATTRIBUTES::Import(const UniValue & val) { } else { newAttr.key = TokenKeys::PaybackDFIFeePCT; } - attributes[newAttr] = value; + SetValue(newAttr, value); return Res::Ok(); } } - attributes[attribute] = value; + SetValue(attribute, value); return Res::Ok(); } ); @@ -506,6 +507,18 @@ UniValue ATTRIBUTES::Export() const { result.pushKV("paybackfees", AmountsToJSON(paybacks->tokensFee.balances)); result.pushKV("paybacktokens", AmountsToJSON(paybacks->tokensPayback.balances)); ret.pushKV(key, result); + } else if (auto balances = boost::get(&attribute.second)) { + for (const auto& pool : *balances) { + auto& dexTokenA = pool.second.totalTokenA; + auto& dexTokenB = pool.second.totalTokenB; + auto poolkey = KeyBuilder(key, pool.first.v); + ret.pushKV(KeyBuilder(poolkey, "total_commission_a"), ValueFromUint(dexTokenA.commissions)); + ret.pushKV(KeyBuilder(poolkey, "total_commission_b"), ValueFromUint(dexTokenB.commissions)); + ret.pushKV(KeyBuilder(poolkey, "fee_burn_a"), ValueFromUint(dexTokenA.feeburn)); + ret.pushKV(KeyBuilder(poolkey, "fee_burn_b"), ValueFromUint(dexTokenB.feeburn)); + ret.pushKV(KeyBuilder(poolkey, "total_swap_a"), ValueFromUint(dexTokenA.swaps)); + ret.pushKV(KeyBuilder(poolkey, "total_swap_b"), ValueFromUint(dexTokenB.swaps)); + } } } catch (const std::out_of_range&) { // Should not get here, that's mean maps are mismatched diff --git a/src/masternodes/govvariables/attributes.h b/src/masternodes/govvariables/attributes.h index 604b3f2dd2..0a3a318b42 100644 --- a/src/masternodes/govvariables/attributes.h +++ b/src/masternodes/govvariables/attributes.h @@ -32,6 +32,7 @@ enum EconomyKeys : uint8_t { DFIP2203Current = 'c', DFIP2203Burned = 'd', DFIP2203Minted = 'e', + DexTokens = 'f', }; enum DFIPKeys : uint8_t { @@ -113,8 +114,38 @@ struct CTokenPayback { ResVal GetFutureSwapContractAddress(); +struct CDexTokenInfo { + + struct CTokenInfo { + uint64_t swaps; + uint64_t feeburn; + uint64_t commissions; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(swaps); + READWRITE(feeburn); + READWRITE(commissions); + } + }; + + CTokenInfo totalTokenA; + CTokenInfo totalTokenB; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(totalTokenA); + READWRITE(totalTokenB); + } +}; + +using CDexBalances = std::map; using CAttributeType = boost::variant; -using CAttributeValue = boost::variant; +using CAttributeValue = boost::variant; class ATTRIBUTES : public GovVariable, public AutoRegistrator { @@ -144,6 +175,21 @@ class ATTRIBUTES : public GovVariable, public AutoRegistrator + void SetValue(const K& key, T&& value) { + static_assert(std::is_convertible_v); + static_assert(std::is_convertible_v); + changed.insert(key); + attributes[key] = std::forward(value); + } + + template + void EraseKey(const K& key) { + static_assert(std::is_convertible_v); + changed.insert(key); + attributes.erase(key); + } + template [[nodiscard]] bool CheckKey(const K& key) const { static_assert(std::is_convertible_v); @@ -158,10 +204,11 @@ class ATTRIBUTES : public GovVariable, public AutoRegistrator attributes; - private: + friend class CGovView; bool futureBlockUpdated{}; + std::set changed; + std::map attributes; // Defined allowed arguments static const std::map& allowedVersions(); diff --git a/src/masternodes/gv.cpp b/src/masternodes/gv.cpp index d1eb43ac3f..55201edb66 100644 --- a/src/masternodes/gv.cpp +++ b/src/masternodes/gv.cpp @@ -15,7 +15,29 @@ Res CGovView::SetVariable(GovVariable const & var) { - return WriteBy(var.GetName(), var) ? Res::Ok() : Res::Err("can't write to DB"); + auto WriteVar = [this](GovVariable const & var) { + return WriteBy(var.GetName(), var) ? Res::Ok() : Res::Err("can't write to DB"); + }; + if (var.GetName() != "ATTRIBUTES") { + return WriteVar(var); + } + auto attributes = GetAttributes(); + if (!attributes) { + return WriteVar(var); + } + auto& current = dynamic_cast(var); + if (current.changed.empty()) { + return Res::Ok(); + } + for (auto& key : current.changed) { + auto it = current.attributes.find(key); + if (it == current.attributes.end()) { + attributes->attributes.erase(key); + } else { + attributes->attributes[key] = it->second; + } + } + return WriteVar(*attributes); } std::shared_ptr CGovView::GetVariable(std::string const & name) const diff --git a/src/masternodes/mn_checks.cpp b/src/masternodes/mn_checks.cpp index e7167c133b..9802848f96 100644 --- a/src/masternodes/mn_checks.cpp +++ b/src/masternodes/mn_checks.cpp @@ -1584,7 +1584,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor balances.Add(obj.source); } - attributes->attributes[liveKey] = balances; + attributes->SetValue(liveKey, balances); mnview.SetVariable(*attributes); @@ -3175,7 +3175,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor balances.Add(CTokenAmount{loanTokenId, subAmount}); balances.Add(CTokenAmount{paybackTokenId, penalty}); - attributes->attributes[liveKey] = balances; + attributes->SetValue(liveKey, balances); LogPrint(BCLog::LOAN, "CLoanPaybackLoanMessage(): Burning interest and loan in %s directly - total loan %lld (%lld %s), height - %d\n", paybackToken->symbol, subLoan + subInterest, subInToken, paybackToken->symbol, height); @@ -3188,7 +3188,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor balances.tokensPayback.Add(CTokenAmount{loanTokenId, subAmount}); balances.tokensFee.Add(CTokenAmount{paybackTokenId, penalty}); - attributes->attributes[liveKey] = balances; + attributes->SetValue(liveKey, balances); LogPrint(BCLog::LOAN, "CLoanPaybackLoanMessage(): Swapping %s to DFI and burning it - total loan %lld (%lld %s), height - %d\n", paybackToken->symbol, subLoan + subInterest, subInToken, paybackToken->symbol, height); @@ -4064,6 +4064,14 @@ Res CPoolSwap::ExecuteSwap(CCustomCSView& view, std::vector poolIDs, boo mnview.Flush(); } + auto attributes = view.GetAttributes(); + if (!attributes) { + attributes = std::make_shared(); + } + + CDataStructureV0 dexKey{AttributeTypes::Live, ParamIDs::Economy, EconomyKeys::DexTokens}; + auto dexBalances = attributes->GetValue(dexKey, CDexBalances{}); + // Set amount to be swapped in pool CTokenAmount swapAmountResult{obj.idTokenFrom, obj.amountFrom}; @@ -4101,6 +4109,18 @@ Res CPoolSwap::ExecuteSwap(CCustomCSView& view, std::vector poolIDs, boo auto dexfeeInPct = view.GetDexFeeInPct(currentID, swapAmount.nTokenId); + auto& balances = dexBalances[currentID]; + auto forward = swapAmount.nTokenId == pool->idTokenA; + + auto& totalTokenA = forward ? balances.totalTokenA : balances.totalTokenB; + auto& totalTokenB = forward ? balances.totalTokenB : balances.totalTokenA; + + const auto& reserveAmount = forward ? pool->reserveA : pool->reserveB; + const auto& blockCommission = forward ? pool->blockCommissionA : pool->blockCommissionB; + + const auto initReserveAmount = reserveAmount; + const auto initBlockCommission = blockCommission; + // Perform swap poolResult = pool->Swap(swapAmount, dexfeeInPct, poolPrice, [&] (const CTokenAmount& dexfeeInAmount, const CTokenAmount& tokenAmount) { // Save swap amount for next loop @@ -4148,6 +4168,7 @@ Res CPoolSwap::ExecuteSwap(CCustomCSView& view, std::vector poolIDs, boo if (!res) { return res; } + totalTokenA.feeburn += dexfeeInAmount.nValue; } // burn the dex out amount @@ -4156,6 +4177,14 @@ Res CPoolSwap::ExecuteSwap(CCustomCSView& view, std::vector poolIDs, boo if (!res) { return res; } + totalTokenB.feeburn += dexfeeOutAmount.nValue; + } + + totalTokenA.swaps += (reserveAmount - initReserveAmount); + totalTokenA.commissions += (blockCommission - initBlockCommission); + + if (lastSwap && obj.to == Params().GetConsensus().burnAddress) { + totalTokenB.feeburn += swapAmountResult.nValue; } return res; @@ -4176,6 +4205,10 @@ Res CPoolSwap::ExecuteSwap(CCustomCSView& view, std::vector poolIDs, boo } } + if (!testOnly) { + attributes->SetValue(dexKey, std::move(dexBalances)); + view.SetVariable(*attributes); + } // Assign to result for loop testing best pool swap result result = swapAmountResult.nValue; diff --git a/src/validation.cpp b/src/validation.cpp index 978997671a..2b29e9fd67 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2833,8 +2833,8 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl CCustomCSView govCache(cache); // Add to existing ATTRIBUTES instead of overwriting. if (var->GetName() == "ATTRIBUTES") { - auto govVar = mnview.GetVariable(var->GetName()); - if (govVar->Import(var->Export()) && govVar->Validate(govCache) && govVar->Apply(govCache, pindex->nHeight) && govCache.SetVariable(*var)) { + auto govVar = cache.GetVariable(var->GetName()); + if (govVar->Import(var->Export()) && govVar->Validate(govCache) && govVar->Apply(govCache, pindex->nHeight) && govCache.SetVariable(*govVar)) { govCache.Flush(); } } else if (var->Validate(govCache) && var->Apply(govCache, pindex->nHeight) && govCache.SetVariable(*var)) { @@ -3435,11 +3435,11 @@ void CChainState::ProcessFutures(const CBlockIndex* pindex, CCustomCSView& cache cache.EraseFuturesUserValues(key); } - attributes->attributes[burnKey] = burned; - attributes->attributes[mintedKey] = minted; + attributes->SetValue(burnKey, std::move(burned)); + attributes->SetValue(mintedKey, std::move(minted)); if (!unpaidContracts.empty()) { - attributes->attributes[liveKey] = balances; + attributes->SetValue(liveKey, std::move(balances)); } cache.SetVariable(*attributes); diff --git a/test/functional/feature_poolswap.py b/test/functional/feature_poolswap.py index 7566a205ff..dbe1cb3197 100755 --- a/test/functional/feature_poolswap.py +++ b/test/functional/feature_poolswap.py @@ -196,6 +196,11 @@ def run_test(self): # 7 Sync self.sync_blocks([self.nodes[0], self.nodes[2]]) + attributes = self.nodes[0].getgov('ATTRIBUTES')['ATTRIBUTES'] + # silver is tokenB + assert_equal(attributes['v0/live/economy/dex/%s/total_swap_b'%(idGS)], Decimal('9.0')) + assert_equal(attributes['v0/live/economy/dex/%s/total_commission_b'%(idGS)], Decimal('1.0')) + # 8 Checking that poolswap is correct goldCheckN0 = self.nodes[2].getaccount(accountGN0, {}, True)[idGold] silverCheckN0 = self.nodes[2].getaccount(accountGN0, {}, True)[idSilver] @@ -253,6 +258,10 @@ def run_test(self): ) self.nodes[0].generate(1) + attributes = self.nodes[0].getgov('ATTRIBUTES')['ATTRIBUTES'] + assert_equal(attributes['v0/live/economy/dex/%s/total_swap_b'%(idGS)], Decimal('189.0')) + assert_equal(attributes['v0/live/economy/dex/%s/total_commission_b'%(idGS)], Decimal('21.0')) + maxPrice = self.nodes[0].listpoolpairs()['1']['reserveB/reserveA'] # exchange tokens each other should work self.nodes[0].poolswap({ @@ -274,6 +283,12 @@ def run_test(self): }) self.nodes[0].generate(1) + attributes = self.nodes[0].getgov('ATTRIBUTES')['ATTRIBUTES'] + assert_equal(attributes['v0/live/economy/dex/%s/total_swap_a'%(idGS)], Decimal('180.0')) + assert_equal(attributes['v0/live/economy/dex/%s/total_commission_a'%(idGS)], Decimal('20.0')) + assert_equal(attributes['v0/live/economy/dex/%s/total_swap_b'%(idGS)], Decimal('369.0')) + assert_equal(attributes['v0/live/economy/dex/%s/total_commission_b'%(idGS)], Decimal('41.0')) + # Test fort canning max price change disconnect_nodes(self.nodes[0], 1) disconnect_nodes(self.nodes[0], 2) @@ -390,6 +405,12 @@ def run_test(self): }) self.nodes[0].generate(1) + attributes = self.nodes[0].getgov('ATTRIBUTES')['ATTRIBUTES'] + assert_equal(attributes['v0/live/economy/dex/%s/total_swap_a'%(idBL)], Decimal('0.0')) + assert_equal(attributes['v0/live/economy/dex/%s/total_commission_a'%(idBL)], Decimal('0.0')) + assert_equal(attributes['v0/live/economy/dex/%s/total_swap_b'%(idBL)], Decimal('0.00000189')) + assert_equal(attributes['v0/live/economy/dex/%s/total_commission_b'%(idBL)], Decimal('1E-8')) + assert_equal(self.nodes[0].getaccount(new_dest, {}, True)[idBTC], Decimal('0.00000002')) # Reset swap and move to Fort Canning Park Height and try swap again @@ -428,7 +449,9 @@ def run_test(self): self.nodes[0].setgov({"ATTRIBUTES":{'v0/poolpairs/%s/token_a_fee_pct'%(idGS): '0.05', 'v0/poolpairs/%s/token_b_fee_pct'%(idGS): '0.08'}}) self.nodes[0].generate(1) - assert_equal(self.nodes[0].getgov('ATTRIBUTES')['ATTRIBUTES'], {'v0/poolpairs/%s/token_a_fee_pct'%(idGS): '0.05', 'v0/poolpairs/%s/token_b_fee_pct'%(idGS): '0.08'}) + attributes = self.nodes[0].getgov('ATTRIBUTES')['ATTRIBUTES'] + assert_equal(attributes['v0/poolpairs/%s/token_a_fee_pct'%(idGS)], '0.05') + assert_equal(attributes['v0/poolpairs/%s/token_b_fee_pct'%(idGS)], '0.08') result = self.nodes[0].getpoolpair(idGS) assert_equal(result[idGS]['dexFeePctTokenA'], Decimal('0.05')) @@ -461,11 +484,17 @@ def run_test(self): assert_equal(self.nodes[0].getburninfo()['dexfeetokens'].sort(), ['%.8f'%(dexinfee)+symbolGOLD, '%.8f'%(dexoutfee)+symbolSILVER].sort()) + attributes = self.nodes[0].getgov('ATTRIBUTES')['ATTRIBUTES'] + assert_equal(attributes['v0/live/economy/dex/%s/fee_burn_a'%(idGS)], dexinfee) + assert_equal(attributes['v0/live/economy/dex/%s/fee_burn_b'%(idGS)], dexoutfee) + # set 1% token dex fee and commission self.nodes[0].setgov({"ATTRIBUTES":{'v0/poolpairs/%s/token_a_fee_pct'%(idGS): '0.01', 'v0/poolpairs/%s/token_b_fee_pct'%(idGS): '0.01'}}) self.nodes[0].generate(1) - assert_equal(self.nodes[0].getgov('ATTRIBUTES')['ATTRIBUTES'], {'v0/poolpairs/%s/token_a_fee_pct'%(idGS): '0.01', 'v0/poolpairs/%s/token_b_fee_pct'%(idGS): '0.01'}) + attributes = self.nodes[0].getgov('ATTRIBUTES')['ATTRIBUTES'] + assert_equal(attributes['v0/poolpairs/%s/token_a_fee_pct'%(idGS)], '0.01') + assert_equal(attributes['v0/poolpairs/%s/token_b_fee_pct'%(idGS)], '0.01') self.nodes[0].updatepoolpair({"pool": "GS", "commission": 0.01}) self.nodes[0].generate(1) @@ -489,7 +518,11 @@ def run_test(self): self.nodes[0].setgov({"ATTRIBUTES":{'v0/poolpairs/%s/token_a_fee_pct'%(idBL): '0.01', 'v0/poolpairs/%s/token_b_fee_pct'%(idBL): '0.01'}}) self.nodes[0].generate(1) - assert_equal(self.nodes[0].getgov('ATTRIBUTES')['ATTRIBUTES'], {'v0/poolpairs/%s/token_a_fee_pct'%(idGS): '0.01', 'v0/poolpairs/%s/token_b_fee_pct'%(idGS): '0.01', 'v0/poolpairs/%s/token_a_fee_pct'%(idBL): '0.01', 'v0/poolpairs/%s/token_b_fee_pct'%(idBL): '0.01'}) + attributes = self.nodes[0].getgov('ATTRIBUTES')['ATTRIBUTES'] + assert_equal(attributes['v0/poolpairs/%s/token_a_fee_pct'%(idGS)], '0.01') + assert_equal(attributes['v0/poolpairs/%s/token_b_fee_pct'%(idGS)], '0.01') + assert_equal(attributes['v0/poolpairs/%s/token_a_fee_pct'%(idBL)], '0.01') + assert_equal(attributes['v0/poolpairs/%s/token_b_fee_pct'%(idBL)], '0.01') self.nodes[0].invalidateblock(self.nodes[0].getblockhash(self.nodes[0].getblockcount())) self.nodes[0].clearmempool() @@ -528,6 +561,10 @@ def run_test(self): dexoutfee = round(trunc(amountA * Decimal(0.05) * coin) / coin, 8) assert_equal(round(amountA - Decimal(dexoutfee), 8), round(swapped, 8)) + attributes = self.nodes[0].getgov('ATTRIBUTES')['ATTRIBUTES'] + assert_equal(attributes['v0/live/economy/dex/%s/fee_burn_b'%(idBL)], round(dexinfee, 8)) + assert_equal(attributes['v0/live/economy/dex/%s/fee_burn_a'%(idBL)], Decimal(str(round(dexoutfee, 8)))) + # REVERTING: #======================== print ("Reverting...") From 3cce1a465ad5b4cf78b3d8960dd9283bc18c2ee7 Mon Sep 17 00:00:00 2001 From: Anthony Fieroni Date: Wed, 27 Apr 2022 10:39:23 +0300 Subject: [PATCH 02/16] Force reindex when dex live does not present Signed-off-by: Anthony Fieroni --- src/init.cpp | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 2f775dc02d..8387a1d0c1 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -1618,9 +1619,23 @@ bool AppInitMain(InitInterfaces& interfaces) pcustomcsview.reset(); pcustomcsview = MakeUnique(*pcustomcsDB.get()); if (!fReset && !fReindexChainState) { - if (!pcustomcsDB->IsEmpty() && pcustomcsview->GetDbVersion() != CCustomCSView::DbVersion) { - strLoadError = _("Account database is unsuitable").translated; - break; + if (!pcustomcsDB->IsEmpty()) { + if (pcustomcsview->GetDbVersion() != CCustomCSView::DbVersion) { + strLoadError = _("Account database is unsuitable").translated; + break; + } + // force reindex iif there is at least one pool swap + PoolHeightKey anyPoolSwap{DCT_ID{}, ~0u}; + auto it = pcustomcsview->LowerBound(anyPoolSwap); + auto shouldReindex = it.Valid(); + if (auto attributes = pcustomcsview->GetAttributes()) { + CDataStructureV0 dexKey{AttributeTypes::Live, ParamIDs::Economy, EconomyKeys::DexTokens}; + shouldReindex &= !attributes->CheckKey(dexKey); + } + if (shouldReindex) { + strLoadError = _("Live dex needs reindex").translated; + break; + } } } From f1ab5d548c387d155651de8d90a4872b4a7c798a Mon Sep 17 00:00:00 2001 From: Dcorral Date: Fri, 6 May 2022 11:28:11 +0200 Subject: [PATCH 03/16] Add test for swap overflow --- test/functional/feature_poolswap_mainnet.py | 110 +++++++++++++++++--- 1 file changed, 95 insertions(+), 15 deletions(-) diff --git a/test/functional/feature_poolswap_mainnet.py b/test/functional/feature_poolswap_mainnet.py index 88e247a0d4..1afd6605af 100755 --- a/test/functional/feature_poolswap_mainnet.py +++ b/test/functional/feature_poolswap_mainnet.py @@ -15,11 +15,12 @@ class PoolPairTest (DefiTestFramework): def set_test_params(self): - self.FC_HEIGHT = 170 + self.FCH_HEIGHT = 170 + self.FCR_HEIGHT = 200 self.num_nodes = 1 self.setup_clean_chain = True self.extra_args = [ - ['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-bayfrontgardensheight=0', '-dakotaheight=160', '-fortcanningheight=163', '-fortcanninghillheight='+str(self.FC_HEIGHT), '-simulatemainnet', '-jellyfish_regtest=1'] + ['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-bayfrontgardensheight=0', '-dakotaheight=160', '-fortcanningheight=163', '-fortcanninghillheight='+str(self.FCH_HEIGHT), '-fortcanningroadheight='+str(self.FCR_HEIGHT),'-simulatemainnet', '-jellyfish_regtest=1'] ] def create_tokens(self): @@ -119,7 +120,7 @@ def add_1satoshi_liquidity_non_empty_pool(self): def setup(self): - self.nodes[0].generate(self.FC_HEIGHT) + self.nodes[0].generate(self.FCH_HEIGHT) self.create_tokens() self.mint_tokens(100000000) self.create_pool_pairs() @@ -174,18 +175,24 @@ def test_simple_swap_1Satoshi(self): "tokenTo": self.symbol_key_SILVER, },[]) self.nodes[0].generate(1) + from_account = self.nodes[0].getaccount(from_address) - to_account = self.nodes[0].getaccount(to_address) assert_equal(from_account[1], '44999999.99999999@GOLD#128') + + to_account = self.nodes[0].getaccount(to_address) assert_equal(to_account, []) - def test_200_simple_swaps_1Satoshi(self): + poolpair_info = self.nodes[0].getpoolpair("GS") + assert_equal(poolpair_info['1']['reserveA'], Decimal('5000000.00000001')) + assert_equal(poolpair_info['1']['reserveB'], Decimal('500000.00000000')) + + def test_50_simple_swaps_1Satoshi(self): from_address = self.account_gs from_account = self.nodes[0].getaccount(from_address) to_address = self.nodes[0].getnewaddress("") assert_equal(from_account[1], '44999999.99999999@GOLD#128') - for _ in range(200): + for _ in range(50): self.nodes[0].poolswap({ "from": self.account_gs, "tokenFrom": self.symbol_key_GOLD, @@ -194,16 +201,22 @@ def test_200_simple_swaps_1Satoshi(self): "tokenTo": self.symbol_key_SILVER, },[]) self.nodes[0].generate(1) + from_account = self.nodes[0].getaccount(from_address) + assert_equal(from_account[1], '44999999.99999949@GOLD#128') + to_account = self.nodes[0].getaccount(to_address) - assert_equal(from_account[1], '44999999.99999799@GOLD#128') assert_equal(to_account, []) + poolpair_info = self.nodes[0].getpoolpair("GS") + assert_equal(poolpair_info['1']['reserveA'], Decimal('5000000.00000051')) + assert_equal(poolpair_info['1']['reserveB'], Decimal('500000.00000000')) + def test_compositeswap_1Satoshi(self): from_address = self.account_gs from_account = self.nodes[0].getaccount(from_address) to_address = self.nodes[0].getnewaddress("") - assert_equal(from_account[1], '44999999.99999799@GOLD#128') + assert_equal(from_account[1], '44999999.99999949@GOLD#128') testPoolSwapRes = self.nodes[0].testpoolswap({ "from": from_address, @@ -223,18 +236,29 @@ def test_compositeswap_1Satoshi(self): "tokenTo": self.symbol_key_DOGE, },[]) self.nodes[0].generate(1) + from_account = self.nodes[0].getaccount(from_address) + assert_equal(from_account[1], '44999999.99999948@GOLD#128') + to_account = self.nodes[0].getaccount(to_address) - assert_equal(from_account[1], '44999999.99999798@GOLD#128') assert_equal(to_account, []) - def test_200_compositeswaps_1Satoshi(self): + poolpair_info_GS = self.nodes[0].getpoolpair("GS") + assert_equal(poolpair_info_GS['1']['reserveA'], Decimal('5000000.00000052')) + assert_equal(poolpair_info_GS['1']['reserveB'], Decimal('500000.00000000')) + + # Second pool nmo changes as 1 sat is lost in comissions in first swap + poolpair_info_SD = self.nodes[0].getpoolpair("DS") + assert_equal(poolpair_info_SD['2']['reserveA'], Decimal('1000000.00000000')) + assert_equal(poolpair_info_SD['2']['reserveB'], Decimal('100000.00000000')) + + def test_50_compositeswaps_1Satoshi(self): from_address = self.account_gs from_account = self.nodes[0].getaccount(from_address) to_address = self.nodes[0].getnewaddress("") - assert_equal(from_account[1], '44999999.99999798@GOLD#128') + assert_equal(from_account[1], '44999999.99999948@GOLD#128') - for _ in range(200): + for _ in range(50): testPoolSwapRes = self.nodes[0].testpoolswap({ "from": from_address, "tokenFrom": self.symbol_key_GOLD, @@ -244,6 +268,7 @@ def test_200_compositeswaps_1Satoshi(self): }, "auto", True) assert_equal(testPoolSwapRes["amount"], '0.00000000@130') assert_equal(len(testPoolSwapRes["pools"]), 2) + self.nodes[0].compositeswap({ "from": self.account_gs, "tokenFrom": self.symbol_key_GOLD, @@ -251,20 +276,75 @@ def test_200_compositeswaps_1Satoshi(self): "to": to_address, "tokenTo": self.symbol_key_DOGE, },[]) + self.nodes[0].generate(1) + from_account = self.nodes[0].getaccount(from_address) + assert_equal(from_account[1], '44999999.99999898@GOLD#128') + to_account = self.nodes[0].getaccount(to_address) - assert_equal(from_account[1], '44999999.99999598@GOLD#128') assert_equal(to_account, []) + poolpair_info_GS = self.nodes[0].getpoolpair("GS") + assert_equal(poolpair_info_GS['1']['reserveA'], Decimal('5000000.00000102')) + assert_equal(poolpair_info_GS['1']['reserveB'], Decimal('500000.00000000')) + + # Second pool nmo changes as 1 sat is lost in comissions in first swap + poolpair_info_SD = self.nodes[0].getpoolpair("DS") + assert_equal(poolpair_info_SD['2']['reserveA'], Decimal('1000000.00000000')) + assert_equal(poolpair_info_SD['2']['reserveB'], Decimal('100000.00000000')) + + def goto(self, height): + current_block = self.nodes[0].getblockcount() + self.nodes[0].generate((height - current_block) + 1) + + def test_swap_full_amount_of_pool(self): + from_address = self.account_gs + from_account = self.nodes[0].getaccount(from_address) + to_address = self.nodes[0].getnewaddress("") + assert_equal(from_account[1], '44999999.99999898@GOLD#128') + + testPoolSwapRes = self.nodes[0].testpoolswap({ + "from": from_address, + "tokenFrom": self.symbol_key_GOLD, + "amountFrom": Decimal('5000000.00000102'), + "to": to_address, + "tokenTo": self.symbol_key_SILVER, + }, "auto", True) + assert_equal(testPoolSwapRes["amount"], '248743.71859296@129') + assert_equal(len(testPoolSwapRes["pools"]), 1) + + self.nodes[0].compositeswap({ + "from": self.account_gs, + "tokenFrom": self.symbol_key_GOLD, + "amountFrom": Decimal('5000000.00000102'), + "to": to_address, + "tokenTo": self.symbol_key_SILVER, + },[]) + self.nodes[0].generate(1) + + from_account = self.nodes[0].getaccount(from_address) + assert_equal(from_account[1], '40049999.99999765@GOLD#128') + + to_account = self.nodes[0].getaccount(to_address) + assert_equal(to_account, ['248743.71859296@SILVER#129']) + + poolpair_info_GS = self.nodes[0].getpoolpair("GS") + assert_equal(poolpair_info_GS['1']['reserveA'], Decimal('9950000.00000203')) + assert_equal(poolpair_info_GS['1']['reserveB'], Decimal('251256.28140704')) + def run_test(self): self.setup() self.test_swap_with_wrong_amounts() self.test_simple_swap_1Satoshi() - self.test_200_simple_swaps_1Satoshi() + self.test_50_simple_swaps_1Satoshi() self.test_compositeswap_1Satoshi() - self.test_200_compositeswaps_1Satoshi() + self.test_50_compositeswaps_1Satoshi() + + self.goto(self.FCR_HEIGHT) # Move to FCR + + self.test_swap_full_amount_of_pool() if __name__ == '__main__': PoolPairTest ().main () From cea51f08729494f334e8cd3434a9132d9eec4b51 Mon Sep 17 00:00:00 2001 From: Dcorral Date: Mon, 9 May 2022 12:16:48 +0400 Subject: [PATCH 04/16] Refactor poolswap tests Add test cases --- test/functional/feature_poolswap.py | 467 ++++++++++---------- test/functional/feature_poolswap_mainnet.py | 4 +- 2 files changed, 233 insertions(+), 238 deletions(-) diff --git a/test/functional/feature_poolswap.py b/test/functional/feature_poolswap.py index dbe1cb3197..a35f98ac1b 100755 --- a/test/functional/feature_poolswap.py +++ b/test/functional/feature_poolswap.py @@ -34,541 +34,513 @@ def set_test_params(self): ['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-bayfrontgardensheight=0', '-dakotaheight=160', '-fortcanningheight=163', '-fortcanninghillheight=170', '-fortcanningroadheight=177'], ['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-bayfrontgardensheight=0', '-dakotaheight=160', '-fortcanningheight=163', '-fortcanninghillheight=170', '-fortcanningroadheight=177']] - - def run_test(self): + def setup(self): assert_equal(len(self.nodes[0].listtokens()), 1) # only one token == DFI self.setup_tokens() # Stop node #3 for future revert self.stop_node(3) - # CREATION: - #======================== - # 1 Getting new addresses and checking coins - symbolGOLD = "GOLD#" + self.get_id_token("GOLD") - symbolSILVER = "SILVER#" + self.get_id_token("SILVER") - idGold = list(self.nodes[0].gettoken(symbolGOLD).keys())[0] - idSilver = list(self.nodes[0].gettoken(symbolSILVER).keys())[0] - accountGN0 = self.nodes[0].get_genesis_keys().ownerAuthAddress - accountSN1 = self.nodes[1].get_genesis_keys().ownerAuthAddress + self.symbolGOLD = "GOLD#" + self.get_id_token("GOLD") + self.symbolSILVER = "SILVER#" + self.get_id_token("SILVER") + self.idGold = list(self.nodes[0].gettoken(self.symbolGOLD).keys())[0] + self.idSilver = list(self.nodes[0].gettoken(self.symbolSILVER).keys())[0] + + self.init_accounts() + self.create_poolpair() - owner = self.nodes[0].getnewaddress("", "legacy") + def init_accounts(self): + self.accountGN0 = self.nodes[0].get_genesis_keys().ownerAuthAddress + self.accountSN1 = self.nodes[1].get_genesis_keys().ownerAuthAddress + self.owner = self.nodes[0].getnewaddress("", "legacy") # 2 Transferring SILVER from N1 Account to N0 Account - self.nodes[1].accounttoaccount(accountSN1, {accountGN0: "1000@" + symbolSILVER}) + self.nodes[1].accounttoaccount(self.accountSN1, {self.accountGN0: "1000@" + self.symbolSILVER}) self.nodes[1].generate(1) # Transferring GOLD from N0 Account to N1 Account - self.nodes[0].accounttoaccount(accountGN0, {accountSN1: "200@" + symbolGOLD}) + self.nodes[0].accounttoaccount(self.accountGN0, {self.accountSN1: "200@" + self.symbolGOLD}) self.nodes[0].generate(1) - silverCheckN0 = self.nodes[0].getaccount(accountGN0, {}, True)[idSilver] - silverCheckN1 = self.nodes[1].getaccount(accountSN1, {}, True)[idSilver] + self.silverCheckN0 = self.nodes[0].getaccount(self.accountGN0, {}, True)[self.idSilver] + self.silverCheckN1 = self.nodes[1].getaccount(self.accountSN1, {}, True)[self.idSilver] - # 3 Creating poolpair + def create_poolpair(self): self.nodes[0].createpoolpair({ - "tokenA": symbolGOLD, - "tokenB": symbolSILVER, + "tokenA": self.symbolGOLD, + "tokenB": self.symbolSILVER, "commission": 0.1, "status": True, - "ownerAddress": owner, + "ownerAddress": self.owner, "pairSymbol": "GS", }, []) self.nodes[0].generate(1) - # only 4 tokens = DFI, GOLD, SILVER, GS assert_equal(len(self.nodes[0].listtokens()), 4) # check tokens id pool = self.nodes[0].getpoolpair("GS") - idGS = list(self.nodes[0].gettoken("GS").keys())[0] - assert_equal(pool[idGS]['idTokenA'], idGold) - assert_equal(pool[idGS]['idTokenB'], idSilver) + self.idGS = list(self.nodes[0].gettoken("GS").keys())[0] + assert_equal(pool[self.idGS]['idTokenA'], self.idGold) + assert_equal(pool[self.idGS]['idTokenB'], self.idSilver) - # Fail swap: lack of liquidity + def test_swap_with_no_liquidity(self): assert_raises_rpc_error(-32600, "Lack of liquidity", self.nodes[0].poolswap, { - "from": accountGN0, - "tokenFrom": symbolSILVER, + "from": self.accountGN0, + "tokenFrom": self.symbolSILVER, "amountFrom": 10, - "to": accountSN1, - "tokenTo": symbolGOLD, + "to": self.accountSN1, + "tokenTo": self.symbolGOLD, } ) - #list_pool = self.nodes[0].listpoolpairs() - #print (list_pool) - - # 4 Adding liquidity - #list_poolshares = self.nodes[0].listpoolshares() - #print (list_poolshares) - + def test_add_liquidity_from_different_nodes(self): self.nodes[0].addpoolliquidity({ - accountGN0: ["100@" + symbolGOLD, "500@" + symbolSILVER] - }, accountGN0, []) + self.accountGN0: ["100@" + self.symbolGOLD, "500@" + self.symbolSILVER] + }, self.accountGN0, []) self.nodes[0].generate(1) self.sync_blocks([self.nodes[0], self.nodes[1]]) - #list_poolshares = self.nodes[0].listpoolshares() - #print (list_poolshares) - self.nodes[1].addpoolliquidity({ - accountSN1: ["100@" + symbolGOLD, "500@" + symbolSILVER] - }, accountSN1, []) + self.accountSN1: ["100@" + self.symbolGOLD, "500@" + self.symbolSILVER] + }, self.accountSN1, []) self.nodes[1].generate(1) self.sync_blocks([self.nodes[0], self.nodes[1]]) - #list_poolshares = self.nodes[0].listpoolshares() - #print (list_poolshares) + goldCheckN0 = self.nodes[0].getaccount(self.accountGN0, {}, True)[self.idGold] + silverCheckN0 = self.nodes[0].getaccount(self.accountGN0, {}, True)[self.idSilver] - goldCheckN0 = self.nodes[0].getaccount(accountGN0, {}, True)[idGold] - silverCheckN0 = self.nodes[0].getaccount(accountGN0, {}, True)[idSilver] - - # 5 Checking that liquidity is correct assert_equal(goldCheckN0, 700) assert_equal(silverCheckN0, 500) list_pool = self.nodes[0].listpoolpairs() - #print (list_pool) assert_equal(list_pool['1']['reserveA'], 200) # GOLD assert_equal(list_pool['1']['reserveB'], 1000) # SILVER - # 6 Trying to poolswap - + def turn_off_pool_and_try_swap(self): self.nodes[0].updatepoolpair({"pool": "GS", "status": False}) self.nodes[0].generate(1) try: self.nodes[0].poolswap({ - "from": accountGN0, - "tokenFrom": symbolSILVER, + "from": self.accountGN0, + "tokenFrom": self.symbolSILVER, "amountFrom": 10, - "to": accountSN1, - "tokenTo": symbolGOLD, + "to": self.accountSN1, + "tokenTo": self.symbolGOLD, }, []) except JSONRPCException as e: errorString = e.error['message'] assert("turned off" in errorString) + + def turn_on_pool_and_swap(self): self.nodes[0].updatepoolpair({"pool": "GS", "status": True}) self.nodes[0].generate(1) - goldCheckN1 = self.nodes[2].getaccount(accountSN1, {}, True)[idGold] - silverCheckN1 = self.nodes[2].getaccount(accountSN1, {}, True)[idSilver] + goldCheckN1 = self.nodes[2].getaccount(self.accountSN1, {}, True)[self.idGold] + silverCheckN1 = self.nodes[2].getaccount(self.accountSN1, {}, True)[self.idSilver] testPoolSwapRes = self.nodes[0].testpoolswap({ - "from": accountGN0, - "tokenFrom": symbolSILVER, + "from": self.accountGN0, + "tokenFrom": self.symbolSILVER, "amountFrom": 10, - "to": accountSN1, - "tokenTo": symbolGOLD, + "to": self.accountSN1, + "tokenTo": self.symbolGOLD, }) - # this acc will be - goldCheckPS = self.nodes[2].getaccount(accountSN1, {}, True)[idGold] + self.goldCheckPS = self.nodes[2].getaccount(self.accountSN1, {}, True)[self.idGold] testPoolSwapSplit = str(testPoolSwapRes).split("@", 2) - psTestAmount = testPoolSwapSplit[0] + self.psTestAmount = testPoolSwapSplit[0] psTestTokenId = testPoolSwapSplit[1] - assert_equal(psTestTokenId, idGold) + assert_equal(psTestTokenId, self.idGold) testPoolSwapVerbose = self.nodes[0].testpoolswap({ - "from": accountGN0, - "tokenFrom": symbolSILVER, + "from": self.accountGN0, + "tokenFrom": self.symbolSILVER, "amountFrom": 10, - "to": accountSN1, - "tokenTo": symbolGOLD, + "to": self.accountSN1, + "tokenTo": self.symbolGOLD, }, "direct", True) assert_equal(testPoolSwapVerbose["path"], "direct") - assert_equal(testPoolSwapVerbose["pools"][0], idGS) + assert_equal(testPoolSwapVerbose["pools"][0], self.idGS) assert_equal(testPoolSwapVerbose["amount"], testPoolSwapRes) + def test_swap_and_live_dex_data(self): self.nodes[0].poolswap({ - "from": accountGN0, - "tokenFrom": symbolSILVER, + "from": self.accountGN0, + "tokenFrom": self.symbolSILVER, "amountFrom": 10, - "to": accountSN1, - "tokenTo": symbolGOLD, + "to": self.accountSN1, + "tokenTo": self.symbolGOLD, }, []) self.nodes[0].generate(1) - - # 7 Sync self.sync_blocks([self.nodes[0], self.nodes[2]]) attributes = self.nodes[0].getgov('ATTRIBUTES')['ATTRIBUTES'] # silver is tokenB - assert_equal(attributes['v0/live/economy/dex/%s/total_swap_b'%(idGS)], Decimal('9.0')) - assert_equal(attributes['v0/live/economy/dex/%s/total_commission_b'%(idGS)], Decimal('1.0')) + assert_equal(attributes['v0/live/economy/dex/%s/total_swap_b'%(self.idGS)], Decimal('9.0')) + assert_equal(attributes['v0/live/economy/dex/%s/total_commission_b'%(self.idGS)], Decimal('1.0')) - # 8 Checking that poolswap is correct - goldCheckN0 = self.nodes[2].getaccount(accountGN0, {}, True)[idGold] - silverCheckN0 = self.nodes[2].getaccount(accountGN0, {}, True)[idSilver] + goldCheckN0 = self.nodes[2].getaccount(self.accountGN0, {}, True)[self.idGold] + silverCheckN0 = self.nodes[2].getaccount(self.accountGN0, {}, True)[self.idSilver] - goldCheckN1 = self.nodes[2].getaccount(accountSN1, {}, True)[idGold] - silverCheckN1 = self.nodes[2].getaccount(accountSN1, {}, True)[idSilver] + goldCheckN1 = self.nodes[2].getaccount(self.accountSN1, {}, True)[self.idGold] + silverCheckN1 = self.nodes[2].getaccount(self.accountSN1, {}, True)[self.idSilver] list_pool = self.nodes[2].listpoolpairs() - #print (list_pool) - - self.nodes[0].listpoolshares() - #print (list_poolshares) assert_equal(goldCheckN0, 700) assert_equal(str(silverCheckN0), "490.49999997") # TODO: calculate "true" values with trading fee! assert_equal(list_pool['1']['reserveA'] + goldCheckN1 , 300) - assert_equal(Decimal(goldCheckPS) + Decimal(psTestAmount), Decimal(goldCheckN1)) + assert_equal(Decimal(self.goldCheckPS) + Decimal(self.psTestAmount), Decimal(goldCheckN1)) assert_equal(str(silverCheckN1), "500.50000000") assert_equal(list_pool['1']['reserveB'], 1009) #1010 - 1 (commission) - # 9 Fail swap: price higher than indicated + def test_price_higher_than_indicated(self): + list_pool = self.nodes[2].listpoolpairs() price = list_pool['1']['reserveA/reserveB'] try: self.nodes[0].poolswap({ - "from": accountGN0, - "tokenFrom": symbolSILVER, + "from": self.accountGN0, + "tokenFrom": self.symbolSILVER, "amountFrom": 10, - "to": accountSN1, - "tokenTo": symbolGOLD, + "to": self.accountSN1, + "tokenTo": self.symbolGOLD, "maxPrice": price - Decimal('0.1'), }, []) except JSONRPCException as e: errorString = e.error['message'] assert("Price is higher than indicated." in errorString) - # activate max price protection + def test_max_price(self): maxPrice = self.nodes[0].listpoolpairs()['1']['reserveB/reserveA'] self.nodes[0].poolswap({ - "from": accountGN0, - "tokenFrom": symbolSILVER, + "from": self.accountGN0, + "tokenFrom": self.symbolSILVER, "amountFrom": 200, - "to": accountGN0, - "tokenTo": symbolGOLD, + "to": self.accountGN0, + "tokenTo": self.symbolGOLD, "maxPrice": maxPrice, }) assert_raises_rpc_error(-26, 'Price is higher than indicated', self.nodes[0].poolswap, { - "from": accountGN0, - "tokenFrom": symbolSILVER, + "from": self.accountGN0, + "tokenFrom": self.symbolSILVER, "amountFrom": 200, - "to": accountGN0, - "tokenTo": symbolGOLD, + "to": self.accountGN0, + "tokenTo": self.symbolGOLD, "maxPrice": maxPrice, } ) self.nodes[0].generate(1) attributes = self.nodes[0].getgov('ATTRIBUTES')['ATTRIBUTES'] - assert_equal(attributes['v0/live/economy/dex/%s/total_swap_b'%(idGS)], Decimal('189.0')) - assert_equal(attributes['v0/live/economy/dex/%s/total_commission_b'%(idGS)], Decimal('21.0')) + assert_equal(attributes['v0/live/economy/dex/%s/total_swap_b'%(self.idGS)], Decimal('189.0')) + assert_equal(attributes['v0/live/economy/dex/%s/total_commission_b'%(self.idGS)], Decimal('21.0')) maxPrice = self.nodes[0].listpoolpairs()['1']['reserveB/reserveA'] # exchange tokens each other should work self.nodes[0].poolswap({ - "from": accountGN0, - "tokenFrom": symbolGOLD, + "from": self.accountGN0, + "tokenFrom": self.symbolGOLD, "amountFrom": 200, - "to": accountGN0, - "tokenTo": symbolSILVER, + "to": self.accountGN0, + "tokenTo": self.symbolSILVER, "maxPrice": maxPrice, }) self.nodes[0].generate(1) self.nodes[0].poolswap({ - "from": accountGN0, - "tokenFrom": symbolSILVER, + "from": self.accountGN0, + "tokenFrom": self.symbolSILVER, "amountFrom": 200, - "to": accountGN0, - "tokenTo": symbolGOLD, + "to": self.accountGN0, + "tokenTo": self.symbolGOLD, "maxPrice": maxPrice, }) self.nodes[0].generate(1) attributes = self.nodes[0].getgov('ATTRIBUTES')['ATTRIBUTES'] - assert_equal(attributes['v0/live/economy/dex/%s/total_swap_a'%(idGS)], Decimal('180.0')) - assert_equal(attributes['v0/live/economy/dex/%s/total_commission_a'%(idGS)], Decimal('20.0')) - assert_equal(attributes['v0/live/economy/dex/%s/total_swap_b'%(idGS)], Decimal('369.0')) - assert_equal(attributes['v0/live/economy/dex/%s/total_commission_b'%(idGS)], Decimal('41.0')) + assert_equal(attributes['v0/live/economy/dex/%s/total_swap_a'%(self.idGS)], Decimal('180.0')) + assert_equal(attributes['v0/live/economy/dex/%s/total_commission_a'%(self.idGS)], Decimal('20.0')) + assert_equal(attributes['v0/live/economy/dex/%s/total_swap_b'%(self.idGS)], Decimal('369.0')) + assert_equal(attributes['v0/live/economy/dex/%s/total_commission_b'%(self.idGS)], Decimal('41.0')) - # Test fort canning max price change + def test_fort_canning_max_price_change(self): disconnect_nodes(self.nodes[0], 1) disconnect_nodes(self.nodes[0], 2) + destination = self.nodes[0].getnewaddress("", "legacy") - swap_from = 200 - coin = 100000000 + self.swap_from = 200 + self.coin = 100000000 self.nodes[0].poolswap({ - "from": accountGN0, - "tokenFrom": symbolGOLD, - "amountFrom": swap_from, + "from": self.accountGN0, + "tokenFrom": self.symbolGOLD, + "amountFrom": self.swap_from, "to": destination, - "tokenTo": symbolSILVER + "tokenTo": self.symbolSILVER }) self.nodes[0].generate(1) - silver_received = self.nodes[0].getaccount(destination, {}, True)[idSilver] - silver_per_gold = round((swap_from * coin) / (silver_received * coin), 8) + silver_received = self.nodes[0].getaccount(destination, {}, True)[self.idSilver] + self.silver_per_gold = round((self.swap_from * self.coin) / (silver_received * self.coin), 8) # Reset swap and try again with max price set to expected amount self.nodes[0].invalidateblock(self.nodes[0].getblockhash(self.nodes[0].getblockcount())) self.nodes[0].clearmempool() self.nodes[0].poolswap({ - "from": accountGN0, - "tokenFrom": symbolGOLD, - "amountFrom": swap_from, + "from": self.accountGN0, + "tokenFrom": self.symbolGOLD, + "amountFrom": self.swap_from, "to": destination, - "tokenTo": symbolSILVER, - "maxPrice": silver_per_gold, + "tokenTo": self.symbolSILVER, + "maxPrice": self.silver_per_gold, }) self.nodes[0].generate(1) + def test_fort_canning_max_price_one_satoshi_below(self): # Reset swap and try again with max price set to one Satoshi below self.nodes[0].invalidateblock(self.nodes[0].getblockhash(self.nodes[0].getblockcount())) self.nodes[0].clearmempool() + destination = self.nodes[0].getnewaddress("", "legacy") try: self.nodes[0].poolswap({ - "from": accountGN0, - "tokenFrom": symbolGOLD, - "amountFrom": swap_from, + "from": self.accountGN0, + "tokenFrom": self.symbolGOLD, + "amountFrom": 200, "to": destination, - "tokenTo": symbolSILVER, - "maxPrice": silver_per_gold - Decimal('0.00000001'), + "tokenTo": self.symbolSILVER, + "maxPrice": self.silver_per_gold - Decimal('0.00000001'), }) except JSONRPCException as e: errorString = e.error['message'] assert("Price is higher than indicated" in errorString) - # Test round up setup + def setup_new_pool_BTC_LTC(self): self.nodes[0].createtoken({ "symbol": "BTC", "name": "Bitcoin", - "collateralAddress": accountGN0 + "collateralAddress": self.accountGN0 }) self.nodes[0].createtoken({ "symbol": "LTC", "name": "Litecoin", - "collateralAddress": accountGN0 + "collateralAddress": self.accountGN0 }) self.nodes[0].generate(1) - symbolBTC = "BTC#" + self.get_id_token("BTC") - symbolLTC = "LTC#" + self.get_id_token("LTC") - idBTC = list(self.nodes[0].gettoken(symbolBTC).keys())[0] - idLTC = list(self.nodes[0].gettoken(symbolLTC).keys())[0] + self.symbolBTC = "BTC#" + self.get_id_token("BTC") + self.symbolLTC = "LTC#" + self.get_id_token("LTC") + self.idBTC = list(self.nodes[0].gettoken(self.symbolBTC).keys())[0] + self.idLTC = list(self.nodes[0].gettoken(self.symbolLTC).keys())[0] - self.nodes[0].minttokens("1@" + symbolBTC) - self.nodes[0].minttokens("111@" + symbolLTC) + self.nodes[0].minttokens("1@" + self.symbolBTC) + self.nodes[0].minttokens("111@" + self.symbolLTC) self.nodes[0].generate(1) self.nodes[0].createpoolpair({ - "tokenA": symbolBTC, - "tokenB": symbolLTC, + "tokenA": self.symbolBTC, + "tokenB": self.symbolLTC, "commission": 0.01, "status": True, - "ownerAddress": owner, + "ownerAddress": self.owner, "pairSymbol": "BTC-LTC", }, []) self.nodes[0].generate(1) - idBL = list(self.nodes[0].gettoken("BTC-LTC").keys())[0] + self.idBL = list(self.nodes[0].gettoken("BTC-LTC").keys())[0] self.nodes[0].addpoolliquidity({ - accountGN0: ["1@" + symbolBTC, "100@" + symbolLTC] - }, accountGN0, []) + self.accountGN0: ["1@" + self.symbolBTC, "100@" + self.symbolLTC] + }, self.accountGN0, []) self.nodes[0].generate(1) - # Test round up + def one_satoshi_swap(self): new_dest = self.nodes[0].getnewaddress("", "legacy") self.nodes[0].poolswap({ - "from": accountGN0, - "tokenFrom": symbolLTC, + "from": self.accountGN0, + "tokenFrom": self.symbolLTC, "amountFrom": 0.00000001, "to": new_dest, - "tokenTo": symbolBTC + "tokenTo": self.symbolBTC }) self.nodes[0].generate(1) - assert_equal(self.nodes[0].getaccount(new_dest, {}, True)[idBTC], Decimal('0.00000001')) + assert_equal(self.nodes[0].getaccount(new_dest, {}, True)[self.idBTC], Decimal('0.00000001')) + def test_two_satoshi_round_up(self, FCP=False): # Reset swap self.nodes[0].invalidateblock(self.nodes[0].getblockhash(self.nodes[0].getblockcount())) self.nodes[0].clearmempool() - # Test for 2 Sat round up + new_dest = self.nodes[0].getnewaddress("", "legacy") self.nodes[0].poolswap({ - "from": accountGN0, - "tokenFrom": symbolLTC, + "from": self.accountGN0, + "tokenFrom": self.symbolLTC, "amountFrom": 0.00000190, "to": new_dest, - "tokenTo": symbolBTC + "tokenTo": self.symbolBTC }) self.nodes[0].generate(1) attributes = self.nodes[0].getgov('ATTRIBUTES')['ATTRIBUTES'] - assert_equal(attributes['v0/live/economy/dex/%s/total_swap_a'%(idBL)], Decimal('0.0')) - assert_equal(attributes['v0/live/economy/dex/%s/total_commission_a'%(idBL)], Decimal('0.0')) - assert_equal(attributes['v0/live/economy/dex/%s/total_swap_b'%(idBL)], Decimal('0.00000189')) - assert_equal(attributes['v0/live/economy/dex/%s/total_commission_b'%(idBL)], Decimal('1E-8')) + assert_equal(attributes['v0/live/economy/dex/%s/total_swap_a'%(self.idBL)], Decimal('0.0')) + assert_equal(attributes['v0/live/economy/dex/%s/total_commission_a'%(self.idBL)], Decimal('0.0')) + assert_equal(attributes['v0/live/economy/dex/%s/total_swap_b'%(self.idBL)], Decimal('0.00000189')) + assert_equal(attributes['v0/live/economy/dex/%s/total_commission_b'%(self.idBL)], Decimal('1E-8')) - assert_equal(self.nodes[0].getaccount(new_dest, {}, True)[idBTC], Decimal('0.00000002')) + expected_round_up = FCP and Decimal('0.00000001') or Decimal('0.00000002') + assert_equal(self.nodes[0].getaccount(new_dest, {}, True)[self.idBTC], expected_round_up) - # Reset swap and move to Fort Canning Park Height and try swap again + def reset_swap_move_to_FCP_and_swap(self): self.nodes[0].invalidateblock(self.nodes[0].getblockhash(self.nodes[0].getblockcount())) self.nodes[0].clearmempool() self.nodes[0].generate(170 - self.nodes[0].getblockcount()) + new_dest = self.nodes[0].getnewaddress("", "legacy") # Test swap now results in zero amount self.nodes[0].poolswap({ - "from": accountGN0, - "tokenFrom": symbolLTC, + "from": self.accountGN0, + "tokenFrom": self.symbolLTC, "amountFrom": 0.00000001, "to": new_dest, - "tokenTo": symbolBTC + "tokenTo": self.symbolBTC }) self.nodes[0].generate(1) - assert(idBTC not in self.nodes[0].getaccount(new_dest, {}, True)) + assert(self.idBTC not in self.nodes[0].getaccount(new_dest, {}, True)) - # Reset swap - self.nodes[0].invalidateblock(self.nodes[0].getblockhash(self.nodes[0].getblockcount())) - self.nodes[0].clearmempool() - - # Test previous 2 Sat swap now results in 1 Sat - self.nodes[0].poolswap({ - "from": accountGN0, - "tokenFrom": symbolLTC, - "amountFrom": 0.00000190, - "to": new_dest, - "tokenTo": symbolBTC - }) - self.nodes[0].generate(1) - - assert_equal(self.nodes[0].getaccount(new_dest, {}, True)[idBTC], Decimal('0.00000001')) - - self.nodes[0].setgov({"ATTRIBUTES":{'v0/poolpairs/%s/token_a_fee_pct'%(idGS): '0.05', 'v0/poolpairs/%s/token_b_fee_pct'%(idGS): '0.08'}}) + def set_token_and_pool_fees(self): + self.nodes[0].setgov({"ATTRIBUTES":{'v0/poolpairs/%s/token_a_fee_pct'%(self.idGS): '0.05', 'v0/poolpairs/%s/token_b_fee_pct'%(self.idGS): '0.08'}}) self.nodes[0].generate(1) attributes = self.nodes[0].getgov('ATTRIBUTES')['ATTRIBUTES'] - assert_equal(attributes['v0/poolpairs/%s/token_a_fee_pct'%(idGS)], '0.05') - assert_equal(attributes['v0/poolpairs/%s/token_b_fee_pct'%(idGS)], '0.08') + assert_equal(attributes['v0/poolpairs/%s/token_a_fee_pct'%(self.idGS)], '0.05') + assert_equal(attributes['v0/poolpairs/%s/token_b_fee_pct'%(self.idGS)], '0.08') - result = self.nodes[0].getpoolpair(idGS) - assert_equal(result[idGS]['dexFeePctTokenA'], Decimal('0.05')) - assert_equal(result[idGS]['dexFeePctTokenB'], Decimal('0.08')) + result = self.nodes[0].getpoolpair(self.idGS) + assert_equal(result[self.idGS]['dexFeePctTokenA'], Decimal('0.05')) + assert_equal(result[self.idGS]['dexFeePctTokenB'], Decimal('0.08')) + def test_listaccounthistory_and_burninfo(self): + destination = self.nodes[0].getnewaddress("", "legacy") self.nodes[0].poolswap({ - "from": accountGN0, - "tokenFrom": symbolGOLD, - "amountFrom": swap_from, + "from": self.accountGN0, + "tokenFrom": self.symbolGOLD, + "amountFrom": self.swap_from, "to": destination, - "tokenTo": symbolSILVER, + "tokenTo": self.symbolSILVER, }) - commission = round((swap_from * 0.1), 8) - amountA = swap_from - commission + commission = round((self.swap_from * 0.1), 8) + amountA = self.swap_from - commission dexinfee = round(amountA * 0.05, 8) amountA = amountA - dexinfee - pool = self.nodes[0].getpoolpair("GS")[idGS] + pool = self.nodes[0].getpoolpair("GS")[self.idGS] reserveA = pool['reserveA'] reserveB = pool['reserveB'] self.nodes[0].generate(1) - pool = self.nodes[0].getpoolpair("GS")[idGS] + pool = self.nodes[0].getpoolpair("GS")[self.idGS] assert_equal(pool['reserveA'] - reserveA, amountA) - swapped = self.nodes[0].getaccount(destination, {}, True)[idSilver] + swapped = self.nodes[0].getaccount(destination, {}, True)[self.idSilver] amountB = reserveB - pool['reserveB'] dexoutfee = round(amountB * Decimal(0.08), 8) assert_equal(amountB - dexoutfee, swapped) - assert_equal(self.nodes[0].listaccounthistory(accountGN0, {'token':symbolGOLD})[0]['amounts'], ['-200.00000000@'+symbolGOLD]) + assert_equal(self.nodes[0].listaccounthistory(self.accountGN0, {'token':self.symbolGOLD})[0]['amounts'], ['-200.00000000@'+self.symbolGOLD]) - assert_equal(self.nodes[0].getburninfo()['dexfeetokens'].sort(), ['%.8f'%(dexinfee)+symbolGOLD, '%.8f'%(dexoutfee)+symbolSILVER].sort()) + assert_equal(self.nodes[0].getburninfo()['dexfeetokens'].sort(), ['%.8f'%(dexinfee)+self.symbolGOLD, '%.8f'%(dexoutfee)+self.symbolSILVER].sort()) attributes = self.nodes[0].getgov('ATTRIBUTES')['ATTRIBUTES'] - assert_equal(attributes['v0/live/economy/dex/%s/fee_burn_a'%(idGS)], dexinfee) - assert_equal(attributes['v0/live/economy/dex/%s/fee_burn_b'%(idGS)], dexoutfee) + assert_equal(attributes['v0/live/economy/dex/%s/fee_burn_a'%(self.idGS)], dexinfee) + assert_equal(attributes['v0/live/economy/dex/%s/fee_burn_b'%(self.idGS)], dexoutfee) - # set 1% token dex fee and commission - self.nodes[0].setgov({"ATTRIBUTES":{'v0/poolpairs/%s/token_a_fee_pct'%(idGS): '0.01', 'v0/poolpairs/%s/token_b_fee_pct'%(idGS): '0.01'}}) + def update_comission_and_fee_to_1pct_pool1(self): + self.nodes[0].setgov({"ATTRIBUTES":{'v0/poolpairs/%s/token_a_fee_pct'%(self.idGS): '0.01', 'v0/poolpairs/%s/token_b_fee_pct'%(self.idGS): '0.01'}}) self.nodes[0].generate(1) attributes = self.nodes[0].getgov('ATTRIBUTES')['ATTRIBUTES'] - assert_equal(attributes['v0/poolpairs/%s/token_a_fee_pct'%(idGS)], '0.01') - assert_equal(attributes['v0/poolpairs/%s/token_b_fee_pct'%(idGS)], '0.01') + assert_equal(attributes['v0/poolpairs/%s/token_a_fee_pct'%(self.idGS)], '0.01') + assert_equal(attributes['v0/poolpairs/%s/token_b_fee_pct'%(self.idGS)], '0.01') self.nodes[0].updatepoolpair({"pool": "GS", "commission": 0.01}) self.nodes[0].generate(1) + destination = self.nodes[0].getnewaddress("", "legacy") # swap 1 sat self.nodes[0].poolswap({ - "from": accountGN0, - "tokenFrom": symbolSILVER, + "from": self.accountGN0, + "tokenFrom": self.symbolSILVER, "amountFrom": 0.00000001, "to": destination, - "tokenTo": symbolGOLD, + "tokenTo": self.symbolGOLD, }) - pool = self.nodes[0].getpoolpair("GS")[idGS] + pool = self.nodes[0].getpoolpair("GS")[self.idGS] reserveA = pool['reserveA'] self.nodes[0].generate(1) - pool = self.nodes[0].getpoolpair("GS")[idGS] + pool = self.nodes[0].getpoolpair("GS")[self.idGS] assert_equal(reserveA, pool['reserveA']) - self.nodes[0].setgov({"ATTRIBUTES":{'v0/poolpairs/%s/token_a_fee_pct'%(idBL): '0.01', 'v0/poolpairs/%s/token_b_fee_pct'%(idBL): '0.01'}}) + self.nodes[0].setgov({"ATTRIBUTES":{'v0/poolpairs/%s/token_a_fee_pct'%(self.idBL): '0.01', 'v0/poolpairs/%s/token_b_fee_pct'%(self.idBL): '0.01'}}) self.nodes[0].generate(1) attributes = self.nodes[0].getgov('ATTRIBUTES')['ATTRIBUTES'] - assert_equal(attributes['v0/poolpairs/%s/token_a_fee_pct'%(idGS)], '0.01') - assert_equal(attributes['v0/poolpairs/%s/token_b_fee_pct'%(idGS)], '0.01') - assert_equal(attributes['v0/poolpairs/%s/token_a_fee_pct'%(idBL)], '0.01') - assert_equal(attributes['v0/poolpairs/%s/token_b_fee_pct'%(idBL)], '0.01') + assert_equal(attributes['v0/poolpairs/%s/token_a_fee_pct'%(self.idGS)], '0.01') + assert_equal(attributes['v0/poolpairs/%s/token_b_fee_pct'%(self.idGS)], '0.01') + assert_equal(attributes['v0/poolpairs/%s/token_a_fee_pct'%(self.idBL)], '0.01') + assert_equal(attributes['v0/poolpairs/%s/token_b_fee_pct'%(self.idBL)], '0.01') + def update_comission_and_fee_to_1pct_pool2(self): self.nodes[0].invalidateblock(self.nodes[0].getblockhash(self.nodes[0].getblockcount())) self.nodes[0].clearmempool() self.nodes[0].generate(1) - self.nodes[0].setgov({"ATTRIBUTES":{'v0/token/%s/dex_in_fee_pct'%(idLTC): '0.02', 'v0/token/%s/dex_out_fee_pct'%(idBTC): '0.05'}}) + self.nodes[0].setgov({"ATTRIBUTES":{'v0/token/%s/dex_in_fee_pct'%(self.idLTC): '0.02', 'v0/token/%s/dex_out_fee_pct'%(self.idBTC): '0.05'}}) self.nodes[0].generate(1) - result = self.nodes[0].getpoolpair(idBL) - assert_equal(result[idBL]['dexFeeInPctTokenB'], Decimal('0.02')) - assert_equal(result[idBL]['dexFeeOutPctTokenA'], Decimal('0.05')) + result = self.nodes[0].getpoolpair(self.idBL) + assert_equal(result[self.idBL]['dexFeeInPctTokenB'], Decimal('0.02')) + assert_equal(result[self.idBL]['dexFeeOutPctTokenA'], Decimal('0.05')) destBTC = self.nodes[0].getnewaddress("", "legacy") swapltc = 10 self.nodes[0].poolswap({ - "from": accountGN0, - "tokenFrom": symbolLTC, + "from": self.accountGN0, + "tokenFrom": self.symbolLTC, "amountFrom": swapltc, "to": destBTC, - "tokenTo": symbolBTC + "tokenTo": self.symbolBTC }) commission = round((swapltc * 0.01), 8) amountB = Decimal(swapltc - commission) dexinfee = amountB * Decimal(0.02) amountB = amountB - dexinfee - pool = self.nodes[0].getpoolpair("BTC-LTC")[idBL] + pool = self.nodes[0].getpoolpair("BTC-LTC")[self.idBL] reserveA = pool['reserveA'] reserveB = pool['reserveB'] self.nodes[0].generate(1) - pool = self.nodes[0].getpoolpair("BTC-LTC")[idBL] + pool = self.nodes[0].getpoolpair("BTC-LTC")[self.idBL] assert_equal(pool['reserveB'] - reserveB, round(amountB, 8)) - swapped = self.nodes[0].getaccount(destBTC, {}, True)[idBTC] + swapped = self.nodes[0].getaccount(destBTC, {}, True)[self.idBTC] amountA = reserveA - pool['reserveA'] - dexoutfee = round(trunc(amountA * Decimal(0.05) * coin) / coin, 8) + dexoutfee = round(trunc(amountA * Decimal(0.05) * self.coin) / self.coin, 8) assert_equal(round(amountA - Decimal(dexoutfee), 8), round(swapped, 8)) attributes = self.nodes[0].getgov('ATTRIBUTES')['ATTRIBUTES'] - assert_equal(attributes['v0/live/economy/dex/%s/fee_burn_b'%(idBL)], round(dexinfee, 8)) - assert_equal(attributes['v0/live/economy/dex/%s/fee_burn_a'%(idBL)], Decimal(str(round(dexoutfee, 8)))) + assert_equal(attributes['v0/live/economy/dex/%s/fee_burn_b'%(self.idBL)], round(dexinfee, 8)) + assert_equal(attributes['v0/live/economy/dex/%s/fee_burn_a'%(self.idBL)], Decimal(str(round(dexoutfee, 8)))) - # REVERTING: - #======================== - print ("Reverting...") - # Reverting creation! + def revert_to_initial_state(self): self.start_node(3) self.nodes[3].generate(30) @@ -578,5 +550,28 @@ def run_test(self): self.sync_blocks() assert_equal(len(self.nodes[0].listpoolpairs()), 0) + + def run_test(self): + self.setup() + self.test_swap_with_no_liquidity() + self.test_add_liquidity_from_different_nodes() + self.turn_off_pool_and_try_swap() + self.turn_on_pool_and_swap() + self.test_swap_and_live_dex_data() + self.test_price_higher_than_indicated() + self.test_max_price() + self.test_fort_canning_max_price_change() + self.test_fort_canning_max_price_one_satoshi_below() + self.setup_new_pool_BTC_LTC() + self.one_satoshi_swap() + self.test_two_satoshi_round_up() + self.reset_swap_move_to_FCP_and_swap() + self.test_two_satoshi_round_up(FCP=True) + self.set_token_and_pool_fees() + self.test_listaccounthistory_and_burninfo() + self.update_comission_and_fee_to_1pct_pool1() + self.update_comission_and_fee_to_1pct_pool2() + self.revert_to_initial_state() + if __name__ == '__main__': PoolPairTest ().main () diff --git a/test/functional/feature_poolswap_mainnet.py b/test/functional/feature_poolswap_mainnet.py index 1afd6605af..53da32f440 100755 --- a/test/functional/feature_poolswap_mainnet.py +++ b/test/functional/feature_poolswap_mainnet.py @@ -298,7 +298,7 @@ def goto(self, height): current_block = self.nodes[0].getblockcount() self.nodes[0].generate((height - current_block) + 1) - def test_swap_full_amount_of_pool(self): + def test_swap_full_amount_of_one_side_of_pool(self): from_address = self.account_gs from_account = self.nodes[0].getaccount(from_address) to_address = self.nodes[0].getnewaddress("") @@ -344,7 +344,7 @@ def run_test(self): self.goto(self.FCR_HEIGHT) # Move to FCR - self.test_swap_full_amount_of_pool() + self.test_swap_full_amount_of_one_side_of_pool() if __name__ == '__main__': PoolPairTest ().main () From 42eb17a8285e6d001cbaa7f660e5eb276a4da1d1 Mon Sep 17 00:00:00 2001 From: Dcorral Date: Mon, 9 May 2022 15:02:04 +0400 Subject: [PATCH 05/16] Remove unused variables fixes lint error --- test/functional/feature_poolswap.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/functional/feature_poolswap.py b/test/functional/feature_poolswap.py index a35f98ac1b..bbc4277557 100755 --- a/test/functional/feature_poolswap.py +++ b/test/functional/feature_poolswap.py @@ -62,7 +62,6 @@ def init_accounts(self): self.nodes[0].generate(1) self.silverCheckN0 = self.nodes[0].getaccount(self.accountGN0, {}, True)[self.idSilver] - self.silverCheckN1 = self.nodes[1].getaccount(self.accountSN1, {}, True)[self.idSilver] def create_poolpair(self): self.nodes[0].createpoolpair({ @@ -138,9 +137,6 @@ def turn_on_pool_and_swap(self): self.nodes[0].updatepoolpair({"pool": "GS", "status": True}) self.nodes[0].generate(1) - goldCheckN1 = self.nodes[2].getaccount(self.accountSN1, {}, True)[self.idGold] - silverCheckN1 = self.nodes[2].getaccount(self.accountSN1, {}, True)[self.idSilver] - testPoolSwapRes = self.nodes[0].testpoolswap({ "from": self.accountGN0, "tokenFrom": self.symbolSILVER, From e312aa46c5db7be283a3eb46dd2964c5495ff360 Mon Sep 17 00:00:00 2001 From: Anthony Fieroni Date: Wed, 11 May 2022 13:49:42 +0300 Subject: [PATCH 06/16] Exclude attributes from merkle root Signed-off-by: Anthony Fieroni --- src/masternodes/masternodes.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/masternodes/masternodes.cpp b/src/masternodes/masternodes.cpp index 54d3bfe875..418275b22d 100644 --- a/src/masternodes/masternodes.cpp +++ b/src/masternodes/masternodes.cpp @@ -1052,9 +1052,13 @@ uint256 CCustomCSView::MerkleRoot() { return {}; } std::vector hashes; - for (const auto& it : rawMap) { - auto value = it.second ? *it.second : TBytes{}; - hashes.push_back(Hash2(it.first, value)); + for (const auto& [key, value] : rawMap) { + MapKV map = {std::make_pair(key, TBytes{})}; + // Attributes should not be part of merkle root + static const std::string attributes("ATTRIBUTES"); + if (!NewKVIterator(attributes, map).Valid()) { + hashes.push_back(Hash2(key, value ? *value : TBytes{})); + } } return ComputeMerkleRoot(std::move(hashes)); } From 53ecb6af3d6ec7591ec03feee848faec226ed34a Mon Sep 17 00:00:00 2001 From: Mihailo Milenkovic Date: Tue, 31 May 2022 17:38:26 +0200 Subject: [PATCH 07/16] Erase attributes from undos so it is not part of merkle root --- src/masternodes/masternodes.cpp | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/masternodes/masternodes.cpp b/src/masternodes/masternodes.cpp index aca7538ec9..c3dba63296 100644 --- a/src/masternodes/masternodes.cpp +++ b/src/masternodes/masternodes.cpp @@ -1049,16 +1049,32 @@ Res CCustomCSView::PopulateCollateralData(CCollateralLoans& result, CVaultId con } uint256 CCustomCSView::MerkleRoot() { - auto& rawMap = GetStorage().GetRaw(); + auto rawMap = GetStorage().GetRaw(); if (rawMap.empty()) { return {}; } - std::vector hashes; - for (const auto& [key, value] : rawMap) { + auto isAttributes = [](const TBytes& key) { MapKV map = {std::make_pair(key, TBytes{})}; // Attributes should not be part of merkle root static const std::string attributes("ATTRIBUTES"); - if (!NewKVIterator(attributes, map).Valid()) { + auto it = NewKVIterator(attributes, map); + return it.Valid() && it.Key() == attributes; + }; + + auto it = NewKVIterator(UndoKey{}, rawMap); + for (; it.Valid(); it.Next()) { + CUndo value = it.Value(); + auto& map = value.before; + for (auto it = map.begin(); it != map.end();) { + isAttributes(it->first) ? map.erase(it++) : ++it; + } + auto key = std::make_pair(CUndosView::ByUndoKey::prefix(), static_cast(it.Key())); + rawMap[DbTypeToBytes(key)] = DbTypeToBytes(value); + } + + std::vector hashes; + for (const auto& [key, value] : rawMap) { + if (!isAttributes(key)) { hashes.push_back(Hash2(key, value ? *value : TBytes{})); } } From 31900010505652ae69b17243c5ed6f018c410e72 Mon Sep 17 00:00:00 2001 From: Peter Bushnell Date: Wed, 1 Jun 2022 12:21:51 +0100 Subject: [PATCH 08/16] Set token split via SetValue --- src/masternodes/govvariables/attributes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/masternodes/govvariables/attributes.cpp b/src/masternodes/govvariables/attributes.cpp index 3d6b69e92b..30c5a2b8c7 100644 --- a/src/masternodes/govvariables/attributes.cpp +++ b/src/masternodes/govvariables/attributes.cpp @@ -595,7 +595,7 @@ Res ATTRIBUTES::Import(const UniValue & val) { const auto& [id, multiplier] = *(splitValue->begin()); tokenSplits.insert(id); - attributes[attribute] = *splitValue; + SetValue(attribute, *splitValue); return Res::Ok(); } From 3bd08aae6d9e1db5f78c94c48895770fc21968dd Mon Sep 17 00:00:00 2001 From: Mihailo Milenkovic Date: Wed, 1 Jun 2022 15:36:02 +0200 Subject: [PATCH 09/16] Fix test --- test/functional/feature_loan_vault.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/functional/feature_loan_vault.py b/test/functional/feature_loan_vault.py index 6994ede2c1..af259a2231 100755 --- a/test/functional/feature_loan_vault.py +++ b/test/functional/feature_loan_vault.py @@ -744,7 +744,7 @@ def loan_and_collateral_token_to_govvar(self): # Invalidate fork block self.nodes[0].invalidateblock(self.nodes[0].getblockhash(self.nodes[0].getblockcount())) - assert_equal(len(self.nodes[0].getgov('ATTRIBUTES')['ATTRIBUTES']), 0) + assert_equal(len(self.nodes[0].getgov('ATTRIBUTES')['ATTRIBUTES']), 12) # Move to hard fork again self.move_to_gw_fork() From c3db655ffb997d52f06f8f03038da03349713eaa Mon Sep 17 00:00:00 2001 From: Mihailo Milenkovic Date: Mon, 13 Jun 2022 17:50:42 +0200 Subject: [PATCH 10/16] Add -dexstats flag for optional usage --- src/init.cpp | 44 ++++++++++++++++------------- src/masternodes/masternodes.cpp | 23 +++++++++++++++ src/masternodes/masternodes.h | 19 ++++++++++++- src/masternodes/mn_checks.cpp | 2 +- src/validation.cpp | 26 +++++++++-------- src/validation.h | 7 +++-- test/functional/feature_poolswap.py | 8 +++--- 7 files changed, 91 insertions(+), 38 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 10b2123910..d49a92561c 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -377,7 +377,7 @@ void SetupServerArgs() // Hidden Options std::vector hidden_args = { - "-dbcrashratio", "-forcecompactdb", + "-dbcrashratio", "-forcecompactdb", "-interrupt-block=", "-stop-block=", "-mocknet", "-mocknet-blocktime=", "-mocknet-key=" // GUI args. These will be overwritten by SetupUIArgs for the GUI @@ -485,6 +485,7 @@ void SetupServerArgs() gArgs.AddArg("-greatworldheight", "Great World fork activation height (regtest only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS); gArgs.AddArg("-jellyfish_regtest", "Configure the regtest network for jellyfish testing", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS); gArgs.AddArg("-simulatemainnet", "Configure the regtest network to mainnet target timespan and spacing ", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS); + gArgs.AddArg("-dexstats", strprintf("Enable storing live dex data in DB (default: %u)", DEFAULT_DEXSTATS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); #ifdef USE_UPNP #if USE_UPNP gArgs.AddArg("-upnp", "Use UPnP to map the listening port (default: 1 when listening and no -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); @@ -1241,7 +1242,7 @@ bool AppInitParameterInteraction() nMaxTipAge = gArgs.GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE); fIsFakeNet = Params().NetworkIDString() == "regtest" && gArgs.GetArg("-dummypos", false); CTxOut::SERIALIZE_FORCED_TO_OLD_IN_TESTS = Params().NetworkIDString() == "regtest" && gArgs.GetArg("-txnotokens", false); - + return true; } @@ -1678,24 +1679,11 @@ bool AppInitMain(InitInterfaces& interfaces) pcustomcsDB = std::make_unique(GetDataDir() / "enhancedcs", nCustomCacheSize, false, fReset || fReindexChainState); pcustomcsview.reset(); pcustomcsview = std::make_unique(*pcustomcsDB.get()); + if (!fReset && !fReindexChainState) { - if (!pcustomcsDB->IsEmpty()) { - if (pcustomcsview->GetDbVersion() != CCustomCSView::DbVersion) { - strLoadError = _("Account database is unsuitable").translated; - break; - } - // force reindex iif there is at least one pool swap - PoolHeightKey anyPoolSwap{DCT_ID{}, ~0u}; - auto it = pcustomcsview->LowerBound(anyPoolSwap); - auto shouldReindex = it.Valid(); - if (auto attributes = pcustomcsview->GetAttributes()) { - CDataStructureV0 dexKey{AttributeTypes::Live, ParamIDs::Economy, EconomyKeys::DexTokens}; - shouldReindex &= !attributes->CheckKey(dexKey); - } - if (shouldReindex) { - strLoadError = _("Live dex needs reindex").translated; - break; - } + if (!pcustomcsDB->IsEmpty() && pcustomcsview->GetDbVersion() != CCustomCSView::DbVersion) { + strLoadError = _("Account database is unsuitable").translated; + break; } } @@ -1744,6 +1732,24 @@ bool AppInitMain(InitInterfaces& interfaces) } assert(::ChainActive().Tip() != nullptr); } + + auto dexStats = gArgs.GetBoolArg("-dexstats", DEFAULT_DEXSTATS); + pcustomcsview->SetDexStatsEnabled(dexStats); + + if (!fReset && !fReindexChainState && !pcustomcsDB->IsEmpty() && dexStats) { + // force reindex if there is no dex data at the tip + PoolHeightKey anyPoolSwap{DCT_ID{}, ~0u}; + auto it = pcustomcsview->LowerBound(anyPoolSwap); + auto shouldReindex = it.Valid(); + auto lastHeight = pcustomcsview->GetDexStatsLastHeight(); + if (lastHeight.has_value()) + shouldReindex &= !(*lastHeight == ::ChainActive().Tip()->nHeight); + + if (shouldReindex) { + strLoadError = _("Live dex needs reindex").translated; + break; + } + } } catch (const std::exception& e) { LogPrintf("%s\n", e.what()); strLoadError = _("Error opening block database").translated; diff --git a/src/masternodes/masternodes.cpp b/src/masternodes/masternodes.cpp index 1040ec35c0..4880ea9b53 100644 --- a/src/masternodes/masternodes.cpp +++ b/src/masternodes/masternodes.cpp @@ -724,6 +724,29 @@ std::vector CAnchorConfirmsView::GetAnchorConfirmData() return confirms; } +/* + * CSettingsView + */ + +void CSettingsView::SetDexStatsLastHeight(const int32_t height) +{ + WriteBy(DEX_STATS_LAST_HEIGHT, height); +} + +std::optional CSettingsView::GetDexStatsLastHeight() +{ + return ReadBy(DEX_STATS_LAST_HEIGHT); +} + +void CSettingsView::SetDexStatsEnabled(const bool enabled) +{ + WriteBy(DEX_STATS_ENABLED, enabled); +} + +std::optional CSettingsView::GetDexStatsEnabled() +{ + return ReadBy(DEX_STATS_ENABLED); +} /* * CCustomCSView */ diff --git a/src/masternodes/masternodes.h b/src/masternodes/masternodes.h index 81bace5c3a..690243217b 100644 --- a/src/masternodes/masternodes.h +++ b/src/masternodes/masternodes.h @@ -297,6 +297,21 @@ class CAnchorConfirmsView : public virtual CStorageView struct BtcTx { static constexpr uint8_t prefix() { return 'x'; } }; }; +class CSettingsView : public virtual CStorageView +{ + +public: + const std::string DEX_STATS_LAST_HEIGHT = "DexStatsLastHeight"; + const std::string DEX_STATS_ENABLED = "DexStatsEnabled"; + + void SetDexStatsLastHeight(int32_t height); + std::optional GetDexStatsLastHeight(); + void SetDexStatsEnabled(bool enabled); + std::optional GetDexStatsEnabled(); + + struct KVSettings { static constexpr uint8_t prefix() { return '0'; } }; +}; + class CCollateralLoans { // in USD double calcRatio(uint64_t maxRatio) const; @@ -351,6 +366,7 @@ class CCustomCSView , public CICXOrderView , public CLoanView , public CVaultView + , public CSettingsView { void CheckPrefixes() { @@ -379,7 +395,8 @@ class CCustomCSView CLoanView :: LoanSetCollateralTokenCreationTx, LoanSetCollateralTokenKey, LoanSetLoanTokenCreationTx, LoanSetLoanTokenKey, LoanSchemeKey, DefaultLoanSchemeKey, DelayedLoanSchemeKey, DestroyLoanSchemeKey, LoanInterestByVault, LoanTokenAmount, LoanLiquidationPenalty, LoanInterestV2ByVault, - CVaultView :: VaultKey, OwnerVaultKey, CollateralKey, AuctionBatchKey, AuctionHeightKey, AuctionBidKey + CVaultView :: VaultKey, OwnerVaultKey, CollateralKey, AuctionBatchKey, AuctionHeightKey, AuctionBidKey, + CSettingsView :: KVSettings >(); } private: diff --git a/src/masternodes/mn_checks.cpp b/src/masternodes/mn_checks.cpp index 3ee27f6715..cb8488af1d 100644 --- a/src/masternodes/mn_checks.cpp +++ b/src/masternodes/mn_checks.cpp @@ -4402,7 +4402,7 @@ Res CPoolSwap::ExecuteSwap(CCustomCSView& view, std::vector poolIDs, boo } } - if (!testOnly) { + if (!testOnly && view.GetDexStatsEnabled().value_or(false)) { attributes->SetValue(dexKey, std::move(dexBalances)); view.SetVariable(*attributes); } diff --git a/src/validation.cpp b/src/validation.cpp index 4b986942c9..28644d8124 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2730,6 +2730,10 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl txdata.reserve(block.vtx.size()); // Required so that pointers to individual PrecomputedTransactionData don't get invalidated + // Set height for live dex data + if (mnview.GetDexStatsEnabled().value_or(false)) + mnview.SetDexStatsLastHeight(pindex->nHeight); + // Execute TXs for (unsigned int i = 0; i < block.vtx.size(); i++) { @@ -2871,7 +2875,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl } UpdateCoins(tx, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight); } - + int64_t nTime3 = GetTimeMicros(); nTimeConnect += nTime3 - nTime2; LogPrint(BCLog::BENCH, " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs (%.2fms/blk)]\n", (unsigned)block.vtx.size(), MILLI * (nTime3 - nTime2), MILLI * (nTime3 - nTime2) / block.vtx.size(), nInputs <= 1 ? 0 : MILLI * (nTime3 - nTime2) / (nInputs-1), nTimeConnect * MICRO, nTimeConnect * MILLI / nBlocksTotal); @@ -3391,7 +3395,7 @@ void CChainState::ProcessLoanEvents(const CBlockIndex* pindex, CCustomCSView& ca auto penaltyAmount = MultiplyAmounts(batch->loanAmount.nValue, COIN + data.liquidationPenalty); if (bidTokenAmount.nValue < penaltyAmount) { - LogPrintf("WARNING: bidTokenAmount.nValue(%d) < penaltyAmount(%d)\n", + LogPrintf("WARNING: bidTokenAmount.nValue(%d) < penaltyAmount(%d)\n", bidTokenAmount.nValue, penaltyAmount); } // penaltyAmount includes interest, batch as well, so we should put interest back @@ -3400,7 +3404,7 @@ void CChainState::ProcessLoanEvents(const CBlockIndex* pindex, CCustomCSView& ca if (amountToBurn > 0) { CScript tmpAddress(vaultId.begin(), vaultId.end()); view.AddBalance(tmpAddress, {bidTokenAmount.nTokenId, amountToBurn}); - SwapToDFIOverUSD(view, bidTokenAmount.nTokenId, amountToBurn, tmpAddress, + SwapToDFIOverUSD(view, bidTokenAmount.nTokenId, amountToBurn, tmpAddress, chainparams.GetConsensus().burnAddress, pindex->nHeight); } @@ -3875,11 +3879,11 @@ size_t RewardConsolidationWorkersCount() { // Note: Be careful with lambda captures and default args. GCC 11.2.0, appears the if the captures are // unused in the function directly, but inside the lambda, it completely disassociates them from the fn // possibly when the lambda is lifted up and with default args, ends up inling the default arg -// completely. TODO: verify with smaller test case. +// completely. TODO: verify with smaller test case. // But scenario: If `interruptOnShutdown` is set as default arg to false, it will never be set true // on the below as it's inlined by gcc 11.2.0 on Ubuntu 22.04 incorrectly. Behavior is correct -// in lower versions of gcc or across clang. -void ConsolidateRewards(CCustomCSView &view, int height, +// in lower versions of gcc or across clang. +void ConsolidateRewards(CCustomCSView &view, int height, const std::vector> &items, bool interruptOnShutdown, int numWorkers) { int nWorkers = numWorkers < 1 ? RewardConsolidationWorkersCount() : numWorkers; auto rewardsTime = GetTimeMicros(); @@ -4021,7 +4025,7 @@ static Res PoolSplits(CCustomCSView& view, CAmount& totalBalance, ATTRIBUTES& at balancesToMigrate.size(), totalAccounts, nWorkers); // Largest first to make sure we are over MINIMUM_LIQUIDITY on first call to AddLiquidity - std::sort(balancesToMigrate.begin(), balancesToMigrate.end(), + std::sort(balancesToMigrate.begin(), balancesToMigrate.end(), [](const std::pair&a, const std::pair& b){ return a.second > b.second; }); @@ -4248,9 +4252,9 @@ static Res VaultSplits(CCustomCSView& view, ATTRIBUTES& attributes, const DCT_ID auto oldTokenAmount = CTokenAmount{oldTokenId, amount}; auto newTokenAmount = CTokenAmount{newTokenId, newAmount}; - LogPrint(BCLog::TOKENSPLIT, "TokenSplit: V Loan (%s: %s => %s)\n", + LogPrint(BCLog::TOKENSPLIT, "TokenSplit: V Loan (%s: %s => %s)\n", vaultId.ToString(), oldTokenAmount.ToString(), newTokenAmount.ToString()); - + res = view.AddLoanToken(vaultId, newTokenAmount); if (!res) { return res; @@ -4327,7 +4331,7 @@ static Res VaultSplits(CCustomCSView& view, ATTRIBUTES& attributes, const DCT_ID value.loanInterest = newLoanInterest; LogPrint(BCLog::TOKENSPLIT, "TokenSplit: V AuctionL (%s,%d: %s => %s, %d => %d)\n", - key.first.ToString(), key.second, oldLoanAmount.ToString(), + key.first.ToString(), key.second, oldLoanAmount.ToString(), newLoanAmount.ToString(), oldInterest, newLoanInterest); } @@ -4514,7 +4518,7 @@ void CChainState::ProcessTokenSplits(const CBlock& block, const CBlockIndex* pin }); LogPrintf("Token split info: rebalance " /* Continued */ - "(id: %d, symbol: %s, add-accounts: %d, sub-accounts: %d, val: %d)\n", + "(id: %d, symbol: %s, add-accounts: %d, sub-accounts: %d, val: %d)\n", id, newToken.symbol, addAccounts.size(), subAccounts.size(), totalBalance); res = view.AddMintedTokens(newTokenId, totalBalance); diff --git a/src/validation.h b/src/validation.h index 72c1c2bf6a..5c6da01394 100644 --- a/src/validation.h +++ b/src/validation.h @@ -129,6 +129,9 @@ static const unsigned int DEFAULT_BANSCORE_THRESHOLD = 100; static const bool DEFAULT_PERSIST_MEMPOOL = false; /** Default for using fee filter */ static const bool DEFAULT_FEEFILTER = true; +/** Default for using live dex in attributes */ +static const bool DEFAULT_DEXSTATS = false; + /** Maximum number of headers to announce when relaying blocks with headers message.*/ static const unsigned int MAX_BLOCKS_TO_ANNOUNCE = 8; @@ -856,8 +859,8 @@ inline CAmount CalculateCoinbaseReward(const CAmount blockReward, const uint32_t Res AddNonTxToBurnIndex(const CScript& from, const CBalances& amounts); -void ConsolidateRewards(CCustomCSView& view, int height, - const std::vector> &items, +void ConsolidateRewards(CCustomCSView& view, int height, + const std::vector> &items, bool interruptOnShutdown, int numWorkers = 0); #endif // DEFI_VALIDATION_H diff --git a/test/functional/feature_poolswap.py b/test/functional/feature_poolswap.py index bbc4277557..d212dea45c 100755 --- a/test/functional/feature_poolswap.py +++ b/test/functional/feature_poolswap.py @@ -29,10 +29,10 @@ def set_test_params(self): # node2: Non Foundation self.setup_clean_chain = True self.extra_args = [ - ['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-bayfrontgardensheight=0', '-dakotaheight=160', '-fortcanningheight=163', '-fortcanninghillheight=170', '-fortcanningroadheight=177', '-acindex=1'], - ['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-bayfrontgardensheight=0', '-dakotaheight=160', '-fortcanningheight=163', '-fortcanninghillheight=170', '-fortcanningroadheight=177', '-acindex=1'], - ['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-bayfrontgardensheight=0', '-dakotaheight=160', '-fortcanningheight=163', '-fortcanninghillheight=170', '-fortcanningroadheight=177'], - ['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-bayfrontgardensheight=0', '-dakotaheight=160', '-fortcanningheight=163', '-fortcanninghillheight=170', '-fortcanningroadheight=177']] + ['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-bayfrontgardensheight=0', '-dakotaheight=160', '-fortcanningheight=163', '-fortcanninghillheight=170', '-fortcanningroadheight=177', '-acindex=1', '-dexstats'], + ['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-bayfrontgardensheight=0', '-dakotaheight=160', '-fortcanningheight=163', '-fortcanninghillheight=170', '-fortcanningroadheight=177', '-acindex=1', '-dexstats'], + ['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-bayfrontgardensheight=0', '-dakotaheight=160', '-fortcanningheight=163', '-fortcanninghillheight=170', '-fortcanningroadheight=177', '-dexstats'], + ['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-bayfrontgardensheight=0', '-dakotaheight=160', '-fortcanningheight=163', '-fortcanninghillheight=170', '-fortcanningroadheight=177', '-dexstats']] def setup(self): assert_equal(len(self.nodes[0].listtokens()), 1) # only one token == DFI From e51cddb1e72cde395b64ab4e22bb05ae6a4d491a Mon Sep 17 00:00:00 2001 From: Mihailo Milenkovic Date: Tue, 14 Jun 2022 12:14:20 +0200 Subject: [PATCH 11/16] Move SetDexStatsLastHeight at the end of ConnectBlock in the view with undo. --- src/validation.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/validation.cpp b/src/validation.cpp index 28644d8124..e61d7df4a9 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2730,10 +2730,6 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl txdata.reserve(block.vtx.size()); // Required so that pointers to individual PrecomputedTransactionData don't get invalidated - // Set height for live dex data - if (mnview.GetDexStatsEnabled().value_or(false)) - mnview.SetDexStatsLastHeight(pindex->nHeight); - // Execute TXs for (unsigned int i = 0; i < block.vtx.size(); i++) { @@ -3019,6 +3015,10 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl // Loan splits ProcessTokenSplits(block, pindex, cache, creationTxs, chainparams); + // Set height for live dex data + if (cache.GetDexStatsEnabled().value_or(false)) + cache.SetDexStatsLastHeight(pindex->nHeight); + // construct undo auto& flushable = cache.GetStorage(); auto undo = CUndo::Construct(mnview.GetStorage(), flushable.GetRaw()); From 2ab35e84ec9fbe0bc74a43c29e35131daadca61b Mon Sep 17 00:00:00 2001 From: Mihailo Milenkovic Date: Tue, 14 Jun 2022 12:59:19 +0200 Subject: [PATCH 12/16] Fix test --- test/functional/feature_loan_vault.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/functional/feature_loan_vault.py b/test/functional/feature_loan_vault.py index af259a2231..6994ede2c1 100755 --- a/test/functional/feature_loan_vault.py +++ b/test/functional/feature_loan_vault.py @@ -744,7 +744,7 @@ def loan_and_collateral_token_to_govvar(self): # Invalidate fork block self.nodes[0].invalidateblock(self.nodes[0].getblockhash(self.nodes[0].getblockcount())) - assert_equal(len(self.nodes[0].getgov('ATTRIBUTES')['ATTRIBUTES']), 12) + assert_equal(len(self.nodes[0].getgov('ATTRIBUTES')['ATTRIBUTES']), 0) # Move to hard fork again self.move_to_gw_fork() From c0207019921aee2065d4d8726bc0aa015f15de33 Mon Sep 17 00:00:00 2001 From: Prasanna Loganathar Date: Mon, 27 Jun 2022 10:37:53 +0530 Subject: [PATCH 13/16] Fix economy keys --- src/masternodes/govvariables/attributes.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/masternodes/govvariables/attributes.h b/src/masternodes/govvariables/attributes.h index d0d43c83cb..55102ea22d 100644 --- a/src/masternodes/govvariables/attributes.h +++ b/src/masternodes/govvariables/attributes.h @@ -37,15 +37,15 @@ enum OracleIDs : uint8_t { }; enum EconomyKeys : uint8_t { - PaybackDFITokens = 'a', - PaybackTokens = 'b', - DFIP2203Current = 'c', - DFIP2203Burned = 'd', - DFIP2203Minted = 'e', - DexTokens = 'f', + PaybackDFITokens = 'a', + PaybackTokens = 'b', + DFIP2203Current = 'c', + DFIP2203Burned = 'd', + DFIP2203Minted = 'e', DFIP2206FCurrent = 'f', DFIP2206FBurned = 'g', DFIP2206FMinted = 'h', + DexTokens = 'i', }; enum DFIPKeys : uint8_t { From f9f79492c8b92edc3d35994b3bb6efb002c9c69f Mon Sep 17 00:00:00 2001 From: Prasanna Loganathar Date: Mon, 27 Jun 2022 10:40:54 +0530 Subject: [PATCH 14/16] Fix lints --- test/lint/lint-circular-dependencies.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/lint/lint-circular-dependencies.sh b/test/lint/lint-circular-dependencies.sh index 5b8bf08fee..6910c2bb1f 100755 --- a/test/lint/lint-circular-dependencies.sh +++ b/test/lint/lint-circular-dependencies.sh @@ -28,7 +28,7 @@ EXPECTED_CIRCULAR_DEPENDENCIES=( "consensus/tx_verify -> masternodes/masternodes -> validation -> consensus/tx_verify" "consensus/tx_verify -> masternodes/mn_checks -> txmempool -> consensus/tx_verify" "index/txindex -> validation -> index/txindex" - "init -> miner -> masternodes/govvariables/attributes -> masternodes/mn_rpc -> wallet/rpcwallet -> init" + "init -> masternodes/govvariables/attributes -> masternodes/mn_rpc -> wallet/rpcwallet -> init" "masternodes/accountshistory -> masternodes/masternodes -> masternodes/accountshistory" "masternodes/accountshistory -> masternodes/masternodes -> masternodes/mn_checks -> masternodes/accountshistory" "masternodes/accountshistory -> masternodes/masternodes -> validation -> masternodes/accountshistory" From 2258935201247c83e7f3a1939bafe532da996937 Mon Sep 17 00:00:00 2001 From: Peter Bushnell Date: Mon, 27 Jun 2022 06:15:59 +0100 Subject: [PATCH 15/16] Refund DUSD use SetValue --- src/masternodes/govvariables/attributes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/masternodes/govvariables/attributes.cpp b/src/masternodes/govvariables/attributes.cpp index f4225831f5..b7bb71f71b 100644 --- a/src/masternodes/govvariables/attributes.cpp +++ b/src/masternodes/govvariables/attributes.cpp @@ -662,7 +662,7 @@ Res ATTRIBUTES::RefundFuturesDUSD(CCustomCSView &mnview, const uint32_t height) } } - attributes[liveKey] = balances; + SetValue(liveKey, std::move(balances)); return Res::Ok(); } From e1da63dca66ad7251483380d3f2d478f56f57925 Mon Sep 17 00:00:00 2001 From: Prasanna Loganathar Date: Mon, 27 Jun 2022 10:55:16 +0530 Subject: [PATCH 16/16] Update src/masternodes/mn_checks.cpp --- src/masternodes/mn_checks.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/masternodes/mn_checks.cpp b/src/masternodes/mn_checks.cpp index e3561a7164..a0b8025aa5 100644 --- a/src/masternodes/mn_checks.cpp +++ b/src/masternodes/mn_checks.cpp @@ -3990,9 +3990,7 @@ Res CPoolSwap::ExecuteSwap(CCustomCSView& view, std::vector poolIDs, boo } auto attributes = view.GetAttributes(); - if (!attributes) { - attributes = std::make_shared(); - } + assert(attributes); CDataStructureV0 dexKey{AttributeTypes::Live, ParamIDs::Economy, EconomyKeys::DexTokens}; auto dexBalances = attributes->GetValue(dexKey, CDexBalances{});