From 37cb8375f61e481c0a5b036a178505316cbdb9c8 Mon Sep 17 00:00:00 2001 From: Peter Bushnell Date: Tue, 5 Jul 2022 07:02:54 +0100 Subject: [PATCH] Revert "Track token and pool splits in account history (#1332)" This reverts commit 510257492c3889198b561366f9e577649f848aed. --- src/flushablestorage.h | 2 +- src/init.cpp | 2 - src/masternodes/accountshistory.cpp | 172 +++++------ src/masternodes/accountshistory.h | 58 +++- src/masternodes/govvariables/attributes.cpp | 17 +- src/masternodes/masternodes.cpp | 68 ++--- src/masternodes/masternodes.h | 31 +- src/masternodes/mn_checks.cpp | 314 +++++++++++++++++++- src/masternodes/mn_checks.h | 3 +- src/masternodes/rpc_accounts.cpp | 39 ++- src/masternodes/tokens.cpp | 32 ++ src/masternodes/tokens.h | 2 + src/masternodes/vaulthistory.h | 1 - src/test/storage_tests.cpp | 22 ++ src/validation.cpp | 86 ++---- test/functional/feature_burn_address.py | 17 +- test/functional/feature_futures.py | 1 - test/functional/feature_token_fork.py | 50 ++-- test/functional/feature_token_split.py | 79 +---- test/functional/rpc_mn_basic.py | 25 +- test/lint/lint-circular-dependencies.sh | 7 +- 21 files changed, 621 insertions(+), 407 deletions(-) diff --git a/src/flushablestorage.h b/src/flushablestorage.h index b5ebd9bb30e..44b83bae5a4 100644 --- a/src/flushablestorage.h +++ b/src/flushablestorage.h @@ -506,7 +506,7 @@ class CStorageView { } } - virtual bool Flush() { return DB().Flush(); } + bool Flush() { return DB().Flush(); } void Discard() { DB().Discard(); } size_t SizeEstimate() const { return DB().SizeEstimate(); } diff --git a/src/init.cpp b/src/init.cpp index 490c136d3d5..461d64e2f11 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1723,12 +1723,10 @@ bool AppInitMain(InitInterfaces& interfaces) paccountHistoryDB.reset(); if (gArgs.GetBoolArg("-acindex", DEFAULT_ACINDEX)) { paccountHistoryDB = std::make_unique(GetDataDir() / "history", nCustomCacheSize, false, fReset || fReindexChainState); - paccountHistoryDB->CreateMultiIndexIfNeeded(); } pburnHistoryDB.reset(); pburnHistoryDB = std::make_unique(GetDataDir() / "burn", nCustomCacheSize, false, fReset || fReindexChainState); - pburnHistoryDB->CreateMultiIndexIfNeeded(); // Create vault history DB pvaultHistoryDB.reset(); diff --git a/src/masternodes/accountshistory.cpp b/src/masternodes/accountshistory.cpp index b6814cd4171..85ee769769d 100644 --- a/src/masternodes/accountshistory.cpp +++ b/src/masternodes/accountshistory.cpp @@ -8,79 +8,15 @@ #include #include -struct AccountHistoryKeyNew { - uint32_t blockHeight; - CScript owner; - uint32_t txn; // for order in block - - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action) { - if (ser_action.ForRead()) { - READWRITE(WrapBigEndian(blockHeight)); - blockHeight = ~blockHeight; - } else { - uint32_t blockHeight_ = ~blockHeight; - READWRITE(WrapBigEndian(blockHeight_)); - } - - READWRITE(owner); - - if (ser_action.ForRead()) { - READWRITE(WrapBigEndian(txn)); - txn = ~txn; - } else { - uint32_t txn_ = ~txn; - READWRITE(WrapBigEndian(txn_)); - } - } -}; - -static AccountHistoryKeyNew Convert(AccountHistoryKey const & key) { - return {key.blockHeight, key.owner, key.txn}; -} - -static AccountHistoryKey Convert(AccountHistoryKeyNew const & key) { - return {key.owner, key.blockHeight, key.txn}; -} - -void CAccountsHistoryView::CreateMultiIndexIfNeeded() +void CAccountsHistoryView::ForEachAccountHistory(std::function)> callback, AccountHistoryKey const & start) { - AccountHistoryKeyNew anyNewKey{~0u, {}, ~0u}; - if (auto it = LowerBound(anyNewKey); it.Valid()) { - return; - } - - LogPrintf("Adding multi index in progress...\n"); - - auto startTime = GetTimeMillis(); - - AccountHistoryKey startKey{{}, ~0u, ~0u}; - auto it = LowerBound(startKey); - for (; it.Valid(); it.Next()) { - WriteBy(Convert(it.Key()), '\0'); - } - - Flush(); - - LogPrint(BCLog::BENCH, " - Multi index took: %dms\n", GetTimeMillis() - startTime); + ForEach(callback, start); } -void CAccountsHistoryView::ForEachAccountHistory(std::function callback, - const CScript & owner, uint32_t height, uint32_t txn) +Res CAccountsHistoryView::WriteAccountHistory(const AccountHistoryKey& key, const AccountHistoryValue& value) { - if (!owner.empty()) { - ForEach(callback, {owner, height, txn}); - return; - } - - ForEach([&](AccountHistoryKeyNew const & newKey, char) { - auto key = Convert(newKey); - auto value = ReadAccountHistory(key); - assert(value); - return callback(key, *value); - }, {height, owner, txn}); + WriteBy(key, value); + return Res::Ok(); } std::optional CAccountsHistoryView::ReadAccountHistory(AccountHistoryKey const & key) const @@ -88,32 +24,9 @@ std::optional CAccountsHistoryView::ReadAccountHistory(Acco return ReadBy(key); } -Res CAccountsHistoryView::WriteAccountHistory(const AccountHistoryKey& key, const AccountHistoryValue& value) -{ - WriteBy(key, value); - WriteBy(Convert(key), '\0'); - return Res::Ok(); -} - Res CAccountsHistoryView::EraseAccountHistory(const AccountHistoryKey& key) { EraseBy(key); - EraseBy(Convert(key)); - return Res::Ok(); -} - -Res CAccountsHistoryView::EraseAccountHistoryHeight(uint32_t height) -{ - std::vector keysToDelete; - - auto it = LowerBound(AccountHistoryKeyNew{height, {}, ~0u}); - for (; it.Valid() && it.Key().blockHeight == height; it.Next()) { - keysToDelete.push_back(Convert(it.Key())); - } - - for (const auto& key : keysToDelete) { - EraseAccountHistory(key); - } return Res::Ok(); } @@ -162,9 +75,78 @@ bool CAccountsHistoryWriter::Flush() return CCustomCSView::Flush(); } -CAccountHistoryStorage* CAccountsHistoryWriter::GetAccountHistoryStore() { - return writers ? writers->GetAccountHistoryStore() : nullptr; -}; +CAccountsHistoryEraser::CAccountsHistoryEraser(CCustomCSView & storage, uint32_t height, uint32_t txn, CHistoryErasers& erasers) + : CStorageView(new CFlushableStorageKV(static_cast(storage.GetStorage()))), height(height), txn(txn), erasers(erasers) +{ +} + +Res CAccountsHistoryEraser::AddBalance(CScript const & owner, CTokenAmount) +{ + erasers.AddBalance(owner, vaultID); + return Res::Ok(); +} + +Res CAccountsHistoryEraser::SubBalance(CScript const & owner, CTokenAmount) +{ + erasers.SubBalance(owner, vaultID); + return Res::Ok(); +} + +bool CAccountsHistoryEraser::Flush() +{ + erasers.Flush(height, txn, vaultID); + return Res::Ok(); // makes sure no changes are applyed to underlaying view +} + +CHistoryErasers::CHistoryErasers(CAccountHistoryStorage* historyView, CBurnHistoryStorage* burnView, CVaultHistoryStorage* vaultView) + : historyView(historyView), burnView(burnView), vaultView(vaultView) {} + +void CHistoryErasers::AddBalance(const CScript& owner, const uint256& vaultID) +{ + if (historyView) { + accounts.insert(owner); + } + if (burnView && owner == Params().GetConsensus().burnAddress) { + burnAccounts.insert(owner); + } + if (vaultView && !vaultID.IsNull()) { + vaults.insert(vaultID); + } +} + +void CHistoryErasers::SubFeeBurn(const CScript& owner) +{ + if (burnView) { + burnAccounts.insert(owner); + } +} + +void CHistoryErasers::SubBalance(const CScript& owner, const uint256& vaultID) +{ + if (historyView) { + accounts.insert(owner); + } + if (burnView && owner == Params().GetConsensus().burnAddress) { + burnAccounts.insert(owner); + } + if (vaultView && !vaultID.IsNull()) { + vaults.insert(vaultID); + } +} + +void CHistoryErasers::Flush(const uint32_t height, const uint32_t txn, const uint256& vaultID) +{ + if (historyView) { + for (const auto& account : accounts) { + historyView->EraseAccountHistory({account, height, txn}); + } + } + if (burnView) { + for (const auto& account : burnAccounts) { + burnView->EraseAccountHistory({account, height, txn}); + } + } +} CHistoryWriters::CHistoryWriters(CAccountHistoryStorage* historyView, CBurnHistoryStorage* burnView, CVaultHistoryStorage* vaultView) : historyView(historyView), burnView(burnView), vaultView(vaultView) {} diff --git a/src/masternodes/accountshistory.h b/src/masternodes/accountshistory.h index a9c7cf2d702..9cd1a2c5015 100644 --- a/src/masternodes/accountshistory.h +++ b/src/masternodes/accountshistory.h @@ -59,24 +59,19 @@ struct AccountHistoryValue { class CAccountsHistoryView : public virtual CStorageView { public: - void CreateMultiIndexIfNeeded(); - Res EraseAccountHistoryHeight(uint32_t height); - [[nodiscard]] std::optional ReadAccountHistory(AccountHistoryKey const & key) const; Res WriteAccountHistory(AccountHistoryKey const & key, AccountHistoryValue const & value); + std::optional ReadAccountHistory(AccountHistoryKey const & key) const; Res EraseAccountHistory(AccountHistoryKey const & key); - void ForEachAccountHistory(std::function callback, - const CScript& owner = {}, uint32_t height = std::numeric_limits::max(), uint32_t txn = std::numeric_limits::max()); + void ForEachAccountHistory(std::function)> callback, AccountHistoryKey const & start = {}); // tags - struct ByAccountHistoryKey { static constexpr uint8_t prefix() { return 'h'; } }; - struct ByAccountHistoryKeyNew { static constexpr uint8_t prefix() { return 'H'; } }; + struct ByAccountHistoryKey { static constexpr uint8_t prefix() { return 'h'; } }; }; class CAccountHistoryStorage : public CAccountsHistoryView , public CAuctionHistoryView { public: - CAccountHistoryStorage(CAccountHistoryStorage& accountHistory) : CStorageView(new CFlushableStorageKV(accountHistory.DB())) {} CAccountHistoryStorage(const fs::path& dbName, std::size_t cacheSize, bool fMemory = false, bool fWipe = false); }; @@ -87,34 +82,52 @@ class CBurnHistoryStorage : public CAccountsHistoryView }; class CHistoryWriters { - CAccountHistoryStorage* historyView{}; - CBurnHistoryStorage* burnView{}; + CAccountHistoryStorage* historyView; + CBurnHistoryStorage* burnView; std::map diffs; std::map burnDiffs; std::map> vaultDiffs; public: - CVaultHistoryStorage* vaultView{}; + CVaultHistoryStorage* vaultView; CLoanSchemeCreation globalLoanScheme; std::string schemeID; CHistoryWriters(CAccountHistoryStorage* historyView, CBurnHistoryStorage* burnView, CVaultHistoryStorage* vaultView); - CAccountHistoryStorage* GetAccountHistoryStore() { return historyView; }; - void AddBalance(const CScript& owner, const CTokenAmount amount, const uint256& vaultID); void AddFeeBurn(const CScript& owner, const CAmount amount); void SubBalance(const CScript& owner, const CTokenAmount amount, const uint256& vaultID); void Flush(const uint32_t height, const uint256& txid, const uint32_t txn, const uint8_t type, const uint256& vaultID); }; +class CHistoryErasers { + CAccountHistoryStorage* historyView; + CBurnHistoryStorage* burnView; + std::set accounts; + std::set burnAccounts; + std::set vaults; + +public: + CVaultHistoryStorage* vaultView; + bool removeLoanScheme{false}; + uint256 schemeCreationTxid; + + CHistoryErasers(CAccountHistoryStorage* historyView, CBurnHistoryStorage* burnView, CVaultHistoryStorage* vaultView); + + void AddBalance(const CScript& owner, const uint256& vaultID); + void SubFeeBurn(const CScript& owner); + void SubBalance(const CScript& owner, const uint256& vaultID); + void Flush(const uint32_t height, const uint32_t txn, const uint256& vaultID); +}; + class CAccountsHistoryWriter : public CCustomCSView { const uint32_t height; const uint32_t txn; const uint256 txid; const uint8_t type; - CHistoryWriters* writers{}; + CHistoryWriters* writers; public: uint256 vaultID; @@ -122,9 +135,22 @@ class CAccountsHistoryWriter : public CCustomCSView CAccountsHistoryWriter(CCustomCSView & storage, uint32_t height, uint32_t txn, const uint256& txid, uint8_t type, CHistoryWriters* writers); Res AddBalance(CScript const & owner, CTokenAmount amount) override; Res SubBalance(CScript const & owner, CTokenAmount amount) override; - bool Flush() override; + bool Flush(); +}; - CAccountHistoryStorage* GetAccountHistoryStore() override; +class CAccountsHistoryEraser : public CCustomCSView +{ + const uint32_t height; + const uint32_t txn; + CHistoryErasers& erasers; + +public: + uint256 vaultID; + + CAccountsHistoryEraser(CCustomCSView & storage, uint32_t height, uint32_t txn, CHistoryErasers& erasers); + Res AddBalance(CScript const & owner, CTokenAmount amount) override; + Res SubBalance(CScript const & owner, CTokenAmount amount) override; + bool Flush(); }; extern std::unique_ptr paccountHistoryDB; diff --git a/src/masternodes/govvariables/attributes.cpp b/src/masternodes/govvariables/attributes.cpp index 97473b5933a..7cfeec9afb4 100644 --- a/src/masternodes/govvariables/attributes.cpp +++ b/src/masternodes/govvariables/attributes.cpp @@ -7,8 +7,8 @@ #include /// CAccountsHistoryWriter #include /// CCustomCSView -#include /// GetAggregatePrice / CustomTxType -#include /// GetNextAccPosition +#include /// GetAggregatePrice +#include /// CustomTxType #include /// GetDecimaleString #include /// ValueFromAmount @@ -586,25 +586,22 @@ Res ATTRIBUTES::RefundFuturesContracts(CCustomCSView &mnview, const uint32_t hei CDataStructureV0 liveKey{AttributeTypes::Live, ParamIDs::Economy, EconomyKeys::DFIP2203Current}; auto balances = GetValue(liveKey, CBalances{}); - CAccountHistoryStorage* historyStore{mnview.GetAccountHistoryStore()}; - const auto currentHeight = mnview.GetLastHeight() + 1; + auto txn = std::numeric_limits::max(); for (const auto& [key, value] : userFuturesValues) { mnview.EraseFuturesUserValues(key); - CHistoryWriters subWriters{historyStore, nullptr, nullptr}; - CAccountsHistoryWriter subView(mnview, currentHeight, GetNextAccPosition(), {}, uint8_t(CustomTxType::FutureSwapRefund), &subWriters); - + CHistoryWriters subWriters{paccountHistoryDB.get(), nullptr, nullptr}; + CAccountsHistoryWriter subView(mnview, height, txn--, {}, uint8_t(CustomTxType::FutureSwapRefund), &subWriters); auto res = subView.SubBalance(*contractAddressValue, value.source); if (!res) { return res; } subView.Flush(); - CHistoryWriters addWriters{historyStore, nullptr, nullptr}; - CAccountsHistoryWriter addView(mnview, currentHeight, GetNextAccPosition(), {}, uint8_t(CustomTxType::FutureSwapRefund), &addWriters); - + CHistoryWriters addWriters{paccountHistoryDB.get(), nullptr, nullptr}; + CAccountsHistoryWriter addView(mnview, height, txn--, {}, uint8_t(CustomTxType::FutureSwapRefund), &addWriters); res = addView.AddBalance(key.owner, value.source); if (!res) { return res; diff --git a/src/masternodes/masternodes.cpp b/src/masternodes/masternodes.cpp index a366c1ec07f..530ac243c3a 100644 --- a/src/masternodes/masternodes.cpp +++ b/src/masternodes/masternodes.cpp @@ -3,11 +3,9 @@ // file LICENSE or http://www.opensource.org/licenses/mit-license.php. #include -#include #include #include #include -#include #include #include @@ -492,6 +490,30 @@ void CMasternodesView::EraseSubNodesLastBlockTime(const uint256& nodeId, const u } } +Res CMasternodesView::UnCreateMasternode(const uint256 & nodeId) +{ + auto node = GetMasternode(nodeId); + if (node) { + EraseBy(nodeId); + EraseBy(node->operatorAuthAddress); + EraseBy(node->ownerAuthAddress); + return Res::Ok(); + } + return Res::Err("No such masternode %s", nodeId.GetHex()); +} + +Res CMasternodesView::UnResignMasternode(const uint256 & nodeId, const uint256 & resignTx) +{ + auto node = GetMasternode(nodeId); + if (node && node->resignTx == resignTx) { + node->resignHeight = -1; + node->resignTx = {}; + WriteBy(nodeId, *node); + return Res::Ok(); + } + return Res::Err("No such masternode %s, resignTx: %s", nodeId.GetHex(), resignTx.GetHex()); +} + uint16_t CMasternodesView::GetTimelock(const uint256& nodeId, const CMasternode& node, const uint64_t height) const { auto timelock = ReadBy(nodeId); @@ -728,26 +750,6 @@ std::optional CSettingsView::GetDexStatsEnabled() /* * CCustomCSView */ -CCustomCSView::CCustomCSView() -{ - CheckPrefixes(); -} - -CCustomCSView::~CCustomCSView() = default; - -CCustomCSView::CCustomCSView(CStorageKV & st) - : CStorageView(new CFlushableStorageKV(st)) -{ - CheckPrefixes(); -} - -// cache-upon-a-cache (not a copy!) constructor -CCustomCSView::CCustomCSView(CCustomCSView & other) - : CStorageView(new CFlushableStorageKV(other.DB())) -{ - CheckPrefixes(); -} - int CCustomCSView::GetDbVersion() const { int version; @@ -1235,25 +1237,3 @@ std::optional CCustomCSView::GetCollater return {}; } - -CAccountHistoryStorage* CCustomCSView::GetAccountHistoryStore() { - return accHistoryStore.get(); -} - -CVaultHistoryStorage* CCustomCSView::GetVaultHistoryStore() { - return vauHistoryStore.get(); -} - -void CCustomCSView::SetAccountHistoryStore() { - if (paccountHistoryDB) { - accHistoryStore.reset(); - accHistoryStore = std::make_unique(*paccountHistoryDB); - } -} - -void CCustomCSView::SetVaultHistoryStore() { - if (pvaultHistoryDB) { - vauHistoryStore.reset(); - vauHistoryStore = std::make_unique(*pvaultHistoryDB); - } -} diff --git a/src/masternodes/masternodes.h b/src/masternodes/masternodes.h index 456d698bc3b..690243217bb 100644 --- a/src/masternodes/masternodes.h +++ b/src/masternodes/masternodes.h @@ -29,10 +29,8 @@ #include #include -class CAccountHistoryStorage; class CBlockIndex; class CTransaction; -class CVaultHistoryStorage; // Works instead of constants cause 'regtest' differs (don't want to overcharge chainparams) int GetMnActivationDelay(int height); @@ -199,6 +197,8 @@ class CMasternodesView : public virtual CStorageView Res CreateMasternode(uint256 const & nodeId, CMasternode const & node, uint16_t timelock); Res ResignMasternode(uint256 const & nodeId, uint256 const & txid, int height); + Res UnCreateMasternode(uint256 const & nodeId); + Res UnResignMasternode(uint256 const & nodeId, uint256 const & resignTx); Res SetForcedRewardAddress(uint256 const & nodeId, const char rewardAddressType, CKeyID const & rewardAddress, int height); Res RemForcedRewardAddress(uint256 const & nodeId, int height); Res UpdateMasternode(uint256 const & nodeId, char operatorType, const CKeyID& operatorAuthAddress, int height); @@ -403,19 +403,27 @@ class CCustomCSView Res PopulateLoansData(CCollateralLoans& result, CVaultId const& vaultId, uint32_t height, int64_t blockTime, bool useNextPrice, bool requireLivePrice); Res PopulateCollateralData(CCollateralLoans& result, CVaultId const& vaultId, CBalances const& collaterals, uint32_t height, int64_t blockTime, bool useNextPrice, bool requireLivePrice); - std::unique_ptr accHistoryStore; - std::unique_ptr vauHistoryStore; public: // Increase version when underlaying tables are changed static constexpr const int DbVersion = 1; - CCustomCSView(); - explicit CCustomCSView(CStorageKV & st); + CCustomCSView() + { + CheckPrefixes(); + } - // cache-upon-a-cache (not a copy!) constructor - CCustomCSView(CCustomCSView & other); + CCustomCSView(CStorageKV & st) + : CStorageView(new CFlushableStorageKV(st)) + { + CheckPrefixes(); + } - ~CCustomCSView(); + // cache-upon-a-cache (not a copy!) constructor + CCustomCSView(CCustomCSView & other) + : CStorageView(new CFlushableStorageKV(other.DB())) + { + CheckPrefixes(); + } // cause depends on current mns: CTeamView::CTeam CalcNextTeam(int height, uint256 const & stakeModifier); @@ -456,11 +464,6 @@ class CCustomCSView return static_cast(DB()); } - virtual CAccountHistoryStorage* GetAccountHistoryStore(); - CVaultHistoryStorage* GetVaultHistoryStore(); - void SetAccountHistoryStore(); - void SetVaultHistoryStore(); - struct DbVersion { static constexpr uint8_t prefix() { return 'D'; } }; }; diff --git a/src/masternodes/mn_checks.cpp b/src/masternodes/mn_checks.cpp index a0b8025aa5f..2cd707c14bb 100644 --- a/src/masternodes/mn_checks.cpp +++ b/src/masternodes/mn_checks.cpp @@ -81,7 +81,6 @@ std::string ToString(CustomTxType type) { case CustomTxType::AuctionBid: return "AuctionBid"; case CustomTxType::FutureSwapExecution: return "FutureSwapExecution"; case CustomTxType::FutureSwapRefund: return "FutureSwapRefund"; - case CustomTxType::TokenSplit: return "TokenSplit"; case CustomTxType::Reject: return "Reject"; case CustomTxType::None: return "None"; } @@ -180,7 +179,6 @@ CCustomTxMessage customTypeToMessage(CustomTxType txType) { case CustomTxType::AuctionBid: return CAuctionBidMessage{}; case CustomTxType::FutureSwapExecution: return CCustomTxMessageNone{}; case CustomTxType::FutureSwapRefund: return CCustomTxMessageNone{}; - case CustomTxType::TokenSplit: return CCustomTxMessageNone{}; case CustomTxType::Reject: return CCustomTxMessageNone{}; case CustomTxType::None: return CCustomTxMessageNone{}; } @@ -3491,6 +3489,270 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor } }; +class CCustomTxRevertVisitor : public CCustomTxVisitor +{ + Res EraseHistory(const CScript& owner) const { + // notify account changes, no matter Sub or Add + return mnview.AddBalance(owner, {}); + } + +public: + using CCustomTxVisitor::CCustomTxVisitor; + + template + Res operator()(const T&) const { + return Res::Ok(); + } + + Res operator()(const CCreateMasterNodeMessage& obj) const { + auto res = CheckMasternodeCreationTx(); + return !res ? res : mnview.UnCreateMasternode(tx.GetHash()); + } + + Res operator()(const CResignMasterNodeMessage& obj) const { + auto res = HasCollateralAuth(obj); + return !res ? res : mnview.UnResignMasternode(obj, tx.GetHash()); + } + + Res operator()(const CCreateTokenMessage& obj) const { + auto res = CheckTokenCreationTx(); + return !res ? res : mnview.RevertCreateToken(tx.GetHash()); + } + + Res operator()(const CCreatePoolPairMessage& obj) const { + //check foundation auth + if (!HasFoundationAuth()) { + return Res::Err("tx not from foundation member"); + } + auto pool = mnview.GetPoolPair(obj.poolPair.idTokenA, obj.poolPair.idTokenB); + if (!pool) { + return Res::Err("no such poolPair tokenA %s, tokenB %s", + obj.poolPair.idTokenA.ToString(), + obj.poolPair.idTokenB.ToString()); + } + return mnview.RevertCreateToken(tx.GetHash()); + } + + Res operator()(const CMintTokensMessage& obj) const { + for (const auto& kv : obj.balances) { + DCT_ID tokenId = kv.first; + auto token = mnview.GetToken(tokenId); + if (!token) { + return Res::Err("token %s does not exist!", tokenId.ToString()); + } + auto tokenImpl = static_cast(*token); + const Coin& coin = coins.AccessCoin(COutPoint(tokenImpl.creationTx, 1)); + EraseHistory(coin.out.scriptPubKey); + } + return Res::Ok(); + } + + Res operator()(const CPoolSwapMessage& obj) const { + EraseHistory(obj.to); + return EraseHistory(obj.from); + } + + Res operator()(const CPoolSwapMessageV2& obj) const { + return (*this)(obj.swapInfo); + } + + Res operator()(const CLiquidityMessage& obj) const { + for (const auto& kv : obj.from) { + EraseHistory(kv.first); + } + return EraseHistory(obj.shareAddress); + } + + Res operator()(const CRemoveLiquidityMessage& obj) const { + return EraseHistory(obj.from); + } + + Res operator()(const CUtxosToAccountMessage& obj) const { + for (const auto& account : obj.to) { + EraseHistory(account.first); + } + return Res::Ok(); + } + + Res operator()(const CAccountToUtxosMessage& obj) const { + return EraseHistory(obj.from); + } + + Res operator()(const CAccountToAccountMessage& obj) const { + for (const auto& account : obj.to) { + EraseHistory(account.first); + } + return EraseHistory(obj.from); + } + + Res operator()(const CSmartContractMessage& obj) const { + for (const auto& account : obj.accounts) { + EraseHistory(account.first); + } + return Res::Ok(); + } + + Res operator()(const CFutureSwapMessage& obj) const { + EraseHistory(obj.owner); + return Res::Ok(); + } + + Res operator()(const CAnyAccountsToAccountsMessage& obj) const { + for (const auto& account : obj.to) { + EraseHistory(account.first); + } + for (const auto& account : obj.from) { + EraseHistory(account.first); + } + return Res::Ok(); + } + + Res operator()(const CICXCreateOrderMessage& obj) const { + if (obj.orderType == CICXOrder::TYPE_INTERNAL) { + auto hash = tx.GetHash(); + EraseHistory({hash.begin(), hash.end()}); + EraseHistory(obj.ownerAddress); + } + return Res::Ok(); + } + + Res operator()(const CICXMakeOfferMessage& obj) const { + auto hash = tx.GetHash(); + EraseHistory({hash.begin(), hash.end()}); + return EraseHistory(obj.ownerAddress); + } + + Res operator()(const CICXSubmitDFCHTLCMessage& obj) const { + auto offer = mnview.GetICXMakeOfferByCreationTx(obj.offerTx); + if (!offer) + return Res::Err("offer with creation tx %s does not exists!", obj.offerTx.GetHex()); + + auto order = mnview.GetICXOrderByCreationTx(offer->orderTx); + if (!order) + return Res::Err("order with creation tx %s does not exists!", offer->orderTx.GetHex()); + + EraseHistory(offer->ownerAddress); + if (order->orderType == CICXOrder::TYPE_INTERNAL) { + CScript orderTxidAddr(order->creationTx.begin(), order->creationTx.end()); + CScript offerTxidAddr(offer->creationTx.begin(), offer->creationTx.end()); + EraseHistory(orderTxidAddr); + EraseHistory(offerTxidAddr); + EraseHistory(consensus.burnAddress); + } + auto hash = tx.GetHash(); + return EraseHistory({hash.begin(), hash.end()}); + } + + Res operator()(const CICXSubmitEXTHTLCMessage& obj) const { + auto offer = mnview.GetICXMakeOfferByCreationTx(obj.offerTx); + if (!offer) + return Res::Err("order with creation tx %s does not exists!", obj.offerTx.GetHex()); + + auto order = mnview.GetICXOrderByCreationTx(offer->orderTx); + if (!order) + return Res::Err("order with creation tx %s does not exists!", offer->orderTx.GetHex()); + + if (order->orderType == CICXOrder::TYPE_EXTERNAL) { + CScript offerTxidAddr(offer->creationTx.begin(), offer->creationTx.end()); + EraseHistory(offerTxidAddr); + EraseHistory(offer->ownerAddress); + EraseHistory(consensus.burnAddress); + } + return Res::Ok(); + } + + Res operator()(const CICXClaimDFCHTLCMessage& obj) const { + auto dfchtlc = mnview.GetICXSubmitDFCHTLCByCreationTx(obj.dfchtlcTx); + if (!dfchtlc) + return Res::Err("dfc htlc with creation tx %s does not exists!", obj.dfchtlcTx.GetHex()); + + auto offer = mnview.GetICXMakeOfferByCreationTx(dfchtlc->offerTx); + if (!offer) + return Res::Err("offer with creation tx %s does not exists!", dfchtlc->offerTx.GetHex()); + + auto order = mnview.GetICXOrderByCreationTx(offer->orderTx); + if (!order) + return Res::Err("order with creation tx %s does not exists!", offer->orderTx.GetHex()); + + CScript htlcTxidAddr(dfchtlc->creationTx.begin(), dfchtlc->creationTx.end()); + EraseHistory(htlcTxidAddr); + EraseHistory(order->ownerAddress); + if (order->orderType == CICXOrder::TYPE_INTERNAL) + EraseHistory(offer->ownerAddress); + return Res::Ok(); + } + + Res operator()(const CICXCloseOrderMessage& obj) const { + std::unique_ptr order; + if (!(order = mnview.GetICXOrderByCreationTx(obj.orderTx))) + return Res::Err("order with creation tx %s does not exists!", obj.orderTx.GetHex()); + + if (order->orderType == CICXOrder::TYPE_INTERNAL) { + CScript txidAddr(order->creationTx.begin(), order->creationTx.end()); + EraseHistory(txidAddr); + EraseHistory(order->ownerAddress); + } + return Res::Ok(); + } + + Res operator()(const CICXCloseOfferMessage& obj) const { + std::unique_ptr offer; + if (!(offer = mnview.GetICXMakeOfferByCreationTx(obj.offerTx))) + return Res::Err("offer with creation tx %s does not exists!", obj.offerTx.GetHex()); + + CScript txidAddr(offer->creationTx.begin(), offer->creationTx.end()); + EraseHistory(txidAddr); + return EraseHistory(offer->ownerAddress); + } + + Res operator()(const CDepositToVaultMessage& obj) const { + return EraseHistory(obj.from); + } + + Res operator()(const CCloseVaultMessage& obj) const { + return EraseHistory(obj.to); + } + + Res operator()(const CLoanTakeLoanMessage& obj) const { + const auto vault = mnview.GetVault(obj.vaultId); + if (!vault) + return Res::Err("Vault <%s> not found", obj.vaultId.GetHex()); + + return EraseHistory(!obj.to.empty() ? obj.to : vault->ownerAddress); + } + + Res operator()(const CWithdrawFromVaultMessage& obj) const { + return EraseHistory(obj.to); + } + + Res operator()(const CLoanPaybackLoanMessage& obj) const { + const auto vault = mnview.GetVault(obj.vaultId); + if (!vault) + return Res::Err("Vault <%s> not found", obj.vaultId.GetHex()); + + EraseHistory(obj.from); + EraseHistory(consensus.burnAddress); + return EraseHistory(vault->ownerAddress); + } + + Res operator()(const CLoanPaybackLoanV2Message& obj) const { + const auto vault = mnview.GetVault(obj.vaultId); + if (!vault) + return Res::Err("Vault <%s> not found", obj.vaultId.GetHex()); + + EraseHistory(obj.from); + EraseHistory(consensus.burnAddress); + return EraseHistory(vault->ownerAddress); + } + + Res operator()(const CAuctionBidMessage& obj) const { + if (auto bid = mnview.GetAuctionBid({obj.vaultId, obj.index})) + EraseHistory(bid->first); + + return EraseHistory(obj.from); + } +}; + Res CustomMetadataParse(uint32_t height, const Consensus::Params& consensus, const std::vector& metadata, CCustomTxMessage& txMessage) { try { return std::visit(CCustomMetadataParseVisitor(height, consensus, metadata), txMessage); @@ -3563,6 +3825,16 @@ Res CustomTxVisit(CCustomCSView& mnview, const CCoinsViewCache& coins, const CTr } } +Res CustomTxRevert(CCustomCSView& mnview, const CCoinsViewCache& coins, const CTransaction& tx, uint32_t height, const Consensus::Params& consensus, const CCustomTxMessage& txMessage) { + try { + return std::visit(CCustomTxRevertVisitor(tx, height, coins, mnview, consensus), txMessage); + } catch (const std::bad_variant_access& e) { + return Res::Err(e.what()); + } catch (...) { + return Res::Err("unexpected error"); + } +} + bool ShouldReturnNonFatalError(const CTransaction& tx, uint32_t height) { static const std::map skippedTx = { { 471222, uint256S("0ab0b76352e2d865761f4c53037041f33e1200183d55cdf6b09500d6f16b7329") }, @@ -3571,6 +3843,44 @@ bool ShouldReturnNonFatalError(const CTransaction& tx, uint32_t height) { return it != skippedTx.end() && it->second == tx.GetHash(); } +Res RevertCustomTx(CCustomCSView& mnview, const CCoinsViewCache& coins, const CTransaction& tx, const Consensus::Params& consensus, uint32_t height, uint32_t txn, CHistoryErasers& erasers) { + if (tx.IsCoinBase() && height > 0) { // genesis contains custom coinbase txs + return Res::Ok(); + } + auto res = Res::Ok(); + std::vector metadata; + auto txType = GuessCustomTxType(tx, metadata); + switch(txType) + { + case CustomTxType::CreateMasternode: + case CustomTxType::ResignMasternode: + case CustomTxType::CreateToken: + case CustomTxType::CreatePoolPair: + // Enable these in the future + case CustomTxType::None: + return res; + default: + break; + } + auto txMessage = customTypeToMessage(txType); + CAccountsHistoryEraser view(mnview, height, txn, erasers); + if ((res = CustomMetadataParse(height, consensus, metadata, txMessage))) { + res = CustomTxRevert(view, coins, tx, height, consensus, txMessage); + + // Track burn fee + if (txType == CustomTxType::CreateToken + || txType == CustomTxType::CreateMasternode + || txType == CustomTxType::Vault) { + erasers.SubFeeBurn(tx.vout[0].scriptPubKey); + } + } + if (!res) { + res.msg = strprintf("%sRevertTx: %s", ToString(txType), res.msg); + return res; + } + return (view.Flush(), res); +} + void PopulateVaultHistoryData(CHistoryWriters* writers, CAccountsHistoryWriter& view, const CCustomTxMessage& txMessage, const CustomTxType txType, const uint32_t height, const uint32_t txn, const uint256& txid) { if (txType == CustomTxType::Vault) { auto obj = std::get(txMessage); diff --git a/src/masternodes/mn_checks.h b/src/masternodes/mn_checks.h index 979a0b54ecd..f3b41319c16 100644 --- a/src/masternodes/mn_checks.h +++ b/src/masternodes/mn_checks.h @@ -103,7 +103,6 @@ enum class CustomTxType : uint8_t // Marker TXs FutureSwapExecution = 'q', FutureSwapRefund = 'w', - TokenSplit = 'P', }; inline CustomTxType CustomTxCodeToType(uint8_t ch) { @@ -161,7 +160,6 @@ inline CustomTxType CustomTxCodeToType(uint8_t ch) { case CustomTxType::AuctionBid: case CustomTxType::FutureSwapExecution: case CustomTxType::FutureSwapRefund: - case CustomTxType::TokenSplit: case CustomTxType::Reject: case CustomTxType::None: return type; @@ -388,6 +386,7 @@ bool IsMempooledCustomTxCreate(const CTxMemPool& pool, const uint256& txid); Res RpcInfo(const CTransaction& tx, uint32_t height, CustomTxType& type, UniValue& results); Res CustomMetadataParse(uint32_t height, const Consensus::Params& consensus, const std::vector& metadata, CCustomTxMessage& txMessage); Res ApplyCustomTx(CCustomCSView& mnview, const CCoinsViewCache& coins, const CTransaction& tx, const Consensus::Params& consensus, uint32_t height, uint64_t time = 0, uint32_t txn = 0, CHistoryWriters* writers = nullptr); +Res RevertCustomTx(CCustomCSView& mnview, const CCoinsViewCache& coins, const CTransaction& tx, const Consensus::Params& consensus, uint32_t height, uint32_t txn, CHistoryErasers& erasers); Res CustomTxVisit(CCustomCSView& mnview, const CCoinsViewCache& coins, const CTransaction& tx, uint32_t height, const Consensus::Params& consensus, const CCustomTxMessage& txMessage, uint64_t time, uint32_t txn = 0); ResVal ApplyAnchorRewardTx(CCustomCSView& mnview, const CTransaction& tx, int height, const uint256& prevStakeModifier, const std::vector& metadata, const Consensus::Params& consensusParams); ResVal ApplyAnchorRewardTxPlus(CCustomCSView& mnview, const CTransaction& tx, int height, const std::vector& metadata, const Consensus::Params& consensusParams); diff --git a/src/masternodes/rpc_accounts.cpp b/src/masternodes/rpc_accounts.cpp index ac527022e55..d2daae9e38d 100644 --- a/src/masternodes/rpc_accounts.cpp +++ b/src/masternodes/rpc_accounts.cpp @@ -1130,14 +1130,14 @@ UniValue listaccounthistory(const JSONRPCRequest& request) { auto count = limit; auto lastHeight = maxBlockHeight; - auto shouldContinueToNextAccountHistory = [&](AccountHistoryKey const & key, AccountHistoryValue value) -> bool { + auto shouldContinueToNextAccountHistory = [&](AccountHistoryKey const & key, CLazySerialize valueLazy) -> bool { if (!isMatchOwner(key.owner)) { return false; } std::unique_ptr reverter; if (!noRewards) { - reverter = std::make_unique(view, key.owner, value.diff); + reverter = std::make_unique(view, key.owner, valueLazy.get().diff); } bool accountRecord = true; @@ -1157,6 +1157,8 @@ UniValue listaccounthistory(const JSONRPCRequest& request) { return true; } + const auto & value = valueLazy.get(); + if (CustomTxType::None != txType && value.category != uint8_t(txType)) { return true; } @@ -1203,10 +1205,12 @@ UniValue listaccounthistory(const JSONRPCRequest& request) { return count != 0 || isMine; }; + AccountHistoryKey startKey{account, maxBlockHeight, txn}; + if (!noRewards && !account.empty()) { // revert previous tx to restore account balances to maxBlockHeight paccountHistoryDB->ForEachAccountHistory([&](AccountHistoryKey const & key, AccountHistoryValue const & value) { - if (maxBlockHeight > key.blockHeight) { + if (startKey.blockHeight > key.blockHeight) { return false; } if (!isMatchOwner(key.owner)) { @@ -1214,10 +1218,10 @@ UniValue listaccounthistory(const JSONRPCRequest& request) { } CScopeAccountReverter(view, key.owner, value.diff); return true; - }, account); + }, {account, std::numeric_limits::max(), std::numeric_limits::max()}); } - paccountHistoryDB->ForEachAccountHistory(shouldContinueToNextAccountHistory, account, maxBlockHeight, txn); + paccountHistoryDB->ForEachAccountHistory(shouldContinueToNextAccountHistory, startKey); if (shouldSearchInWallet) { count = limit; @@ -1405,7 +1409,7 @@ UniValue listburnhistory(const JSONRPCRequest& request) { auto count = limit; - auto shouldContinueToNextAccountHistory = [&](AccountHistoryKey const & key, AccountHistoryValue value) -> bool + auto shouldContinueToNextAccountHistory = [&](AccountHistoryKey const & key, CLazySerialize valueLazy) -> bool { if (!isMatchOwner(key.owner)) { return false; @@ -1415,6 +1419,8 @@ UniValue listburnhistory(const JSONRPCRequest& request) { return true; } + const auto & value = valueLazy.get(); + if (txTypeSearch && value.category != uint8_t(txType)) { return true; } @@ -1431,7 +1437,8 @@ UniValue listburnhistory(const JSONRPCRequest& request) { return count != 0; }; - pburnHistoryDB->ForEachAccountHistory(shouldContinueToNextAccountHistory, {}, maxBlockHeight); + AccountHistoryKey startKey{{}, maxBlockHeight, std::numeric_limits::max()}; + pburnHistoryDB->ForEachAccountHistory(shouldContinueToNextAccountHistory, startKey); UniValue slice(UniValue::VARR); for (auto it = ret.cbegin(); limit != 0 && it != ret.cend(); ++it) { @@ -1544,7 +1551,7 @@ UniValue accounthistorycount(const JSONRPCRequest& request) { auto lastHeight = uint32_t(::ChainActive().Height()); const auto currentHeight = lastHeight; - auto shouldContinueToNextAccountHistory = [&](AccountHistoryKey const & key, AccountHistoryValue value) -> bool { + auto shouldContinueToNextAccountHistory = [&](AccountHistoryKey const & key, CLazySerialize valueLazy) -> bool { if (!owner.empty() && owner != key.owner) { return false; } @@ -1553,6 +1560,8 @@ UniValue accounthistorycount(const JSONRPCRequest& request) { return true; } + const auto& value = valueLazy.get(); + std::unique_ptr reverter; if (!noRewards) { reverter = std::make_unique(view, key.owner, value.diff); @@ -1589,7 +1598,8 @@ UniValue accounthistorycount(const JSONRPCRequest& request) { return true; }; - paccountHistoryDB->ForEachAccountHistory(shouldContinueToNextAccountHistory, owner, currentHeight); + AccountHistoryKey startAccountKey{owner, currentHeight, std::numeric_limits::max()}; + paccountHistoryDB->ForEachAccountHistory(shouldContinueToNextAccountHistory, startAccountKey); if (shouldSearchInWallet) { searchInWallet(pwallet, owner, filter, @@ -1856,7 +1866,8 @@ UniValue getburninfo(const JSONRPCRequest& request) { } } - auto calculateBurnAmounts = [&](AccountHistoryKey const& key, AccountHistoryValue value) { + auto calculateBurnAmounts = [&](AccountHistoryKey const& key, CLazySerialize valueLazy) { + const auto & value = valueLazy.get(); // UTXO burn if (value.category == uint8_t(CustomTxType::None)) { for (auto const & diff : value.diff) { @@ -1903,8 +1914,12 @@ UniValue getburninfo(const JSONRPCRequest& request) { return true; }; - burnView->ForEachAccountHistory(calculateBurnAmounts); - + AccountHistoryKey startKey{{}, + std::numeric_limits::max(), + std::numeric_limits::max()}; + + burnView->ForEachAccountHistory(calculateBurnAmounts, startKey); + UniValue result(UniValue::VOBJ); result.pushKV("address", ScriptToString(burnAddress)); result.pushKV("amount", ValueFromAmount(burntDFI)); diff --git a/src/masternodes/tokens.cpp b/src/masternodes/tokens.cpp index 7c6cb31f11f..4f6ba8ec11b 100644 --- a/src/masternodes/tokens.cpp +++ b/src/masternodes/tokens.cpp @@ -105,6 +105,25 @@ ResVal CTokensView::CreateToken(const CTokensView::CTokenImpl & token, b return {id, Res::Ok()}; } +Res CTokensView::RevertCreateToken(const uint256 & txid) +{ + auto pair = GetTokenByCreationTx(txid); + if (!pair) { + return Res::Err("Token creation revert error: token with creation tx %s does not exist!\n", txid.ToString()); + } + DCT_ID id = pair->first; + auto lastId = ReadLastDctId(); + if (!lastId || (*lastId) != id) { + return Res::Err("Token creation revert error: revert sequence broken! (txid = %s, id = %s, LastDctId = %s)\n", txid.ToString(), id.ToString(), (lastId ? lastId->ToString() : DCT_ID{0}.ToString())); + } + auto const & token = pair->second; + EraseBy(id); + EraseBy(token.symbol); + EraseBy(token.creationTx); + DecrementLastDctId(); + return Res::Ok(); +} + Res CTokensView::UpdateToken(const CTokenImpl& newToken, bool isPreBayfront, const bool tokenSplitUpdate) { auto pair = GetTokenByCreationTx(newToken.creationTx); @@ -242,6 +261,19 @@ DCT_ID CTokensView::IncrementLastDctId() return result; } +DCT_ID CTokensView::DecrementLastDctId() +{ + auto lastDctId = ReadLastDctId(); + if (lastDctId && *lastDctId >= DCT_ID_START) { + --(lastDctId->v); + } else { + LogPrintf("Critical fault: trying to decrement nonexistent DCT_ID or it is lower than DCT_ID_START\n"); + assert (false); + } + assert (Write(LastDctId::prefix(), *lastDctId)); // it is ok if (DCT_ID_START - 1) will be written + return *lastDctId; +} + std::optional CTokensView::ReadLastDctId() const { DCT_ID lastDctId{DCT_ID_START}; diff --git a/src/masternodes/tokens.h b/src/masternodes/tokens.h index ff99b629737..fb5c2afa3ec 100644 --- a/src/masternodes/tokens.h +++ b/src/masternodes/tokens.h @@ -148,6 +148,7 @@ class CTokensView : public virtual CStorageView Res CreateDFIToken(); ResVal CreateToken(CTokenImpl const & token, bool isPreBayfront = false); + Res RevertCreateToken(uint256 const & txid); Res UpdateToken(CTokenImpl const & newToken, bool isPreBayfront = false, const bool tokenSplitUpdatea = false); Res BayfrontFlagsCleanup(); @@ -163,6 +164,7 @@ class CTokensView : public virtual CStorageView private: // have to incapsulate "last token id" related methods here DCT_ID IncrementLastDctId(); + DCT_ID DecrementLastDctId(); std::optional ReadLastDctId() const; }; diff --git a/src/masternodes/vaulthistory.h b/src/masternodes/vaulthistory.h index e6afa58f210..07751e5f755 100644 --- a/src/masternodes/vaulthistory.h +++ b/src/masternodes/vaulthistory.h @@ -183,7 +183,6 @@ class CVaultHistoryView : public virtual CStorageView class CVaultHistoryStorage : public CVaultHistoryView { public: - CVaultHistoryStorage(CVaultHistoryStorage& vaultHistory) : CStorageView(new CFlushableStorageKV(vaultHistory.DB())) {} CVaultHistoryStorage(const fs::path& dbName, std::size_t cacheSize, bool fMemory = false, bool fWipe = false); }; diff --git a/src/test/storage_tests.cpp b/src/test/storage_tests.cpp index 6eba25eb673..1ac2fbc7b08 100644 --- a/src/test/storage_tests.cpp +++ b/src/test/storage_tests.cpp @@ -235,6 +235,28 @@ BOOST_AUTO_TEST_CASE(tokens) BOOST_REQUIRE(pair->second.creationTx == uint256S("0x2222")); } + // revert create token + BOOST_REQUIRE(pcustomcsview->RevertCreateToken(uint256S("0xffff")) == false); + BOOST_REQUIRE(pcustomcsview->RevertCreateToken(uint256S("0x1111")) == false); + BOOST_REQUIRE(pcustomcsview->RevertCreateToken(uint256S("0x2222"))); + BOOST_REQUIRE(GetTokensCount() == 2); + { // search by id + auto token = pcustomcsview->GetToken(DCT_ID{128}); + BOOST_REQUIRE(token); + BOOST_REQUIRE(token->symbol == "DCT1"); + } + + // create again, with same tx and dctid + token1.symbol = "DCT3"; + token1.creationTx = uint256S("0x2222"); // SAME! + BOOST_REQUIRE(pcustomcsview->CreateToken(token1, false).ok); + BOOST_REQUIRE(GetTokensCount() == 3); + { // search by id + auto token = pcustomcsview->GetToken(DCT_ID{129}); + BOOST_REQUIRE(token); + BOOST_REQUIRE(token->symbol == "DCT3"); + } + { // search by id auto token = pcustomcsview->GetToken(DCT_ID{129}); BOOST_REQUIRE(token); diff --git a/src/validation.cpp b/src/validation.cpp index 573cc654eba..5cec0ca4a3e 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1715,13 +1715,9 @@ DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockI // special case: possible undo (first) of custom 'complex changes' for the whole block (expired orders and/or prices) mnview.OnUndoTx(uint256(), (uint32_t) pindex->nHeight); // undo for "zero hash" - pburnHistoryDB->EraseAccountHistoryHeight(pindex->nHeight); - if (paccountHistoryDB) { - paccountHistoryDB->EraseAccountHistoryHeight(pindex->nHeight); - } - if (pindex->nHeight >= Params().GetConsensus().FortCanningHeight) { // erase auction fee history + pburnHistoryDB->EraseAccountHistory({Params().GetConsensus().burnAddress, uint32_t(pindex->nHeight), ~0u}); if (paccountHistoryDB) { paccountHistoryDB->EraseAuctionHistoryHeight(pindex->nHeight); } @@ -1836,6 +1832,11 @@ DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockI // process transactions revert for masternodes mnview.OnUndoTx(tx.GetHash(), (uint32_t) pindex->nHeight); + CHistoryErasers erasers{paccountHistoryDB.get(), pburnHistoryDB.get(), pvaultHistoryDB.get()}; + auto res = RevertCustomTx(mnview, view, tx, Params().GetConsensus(), (uint32_t) pindex->nHeight, i, erasers); + if (!res) { + LogPrintf("%s\n", res.msg); + } } // one time downgrade to revert CInterestRateV2 structure @@ -1852,7 +1853,7 @@ DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockI // Make sure to initialize lastTxOut, otherwise it never finds the block and // ends up looping through uninitialized garbage value. uint32_t lastTxOut = 0; - auto shouldContinueToNextAccountHistory = [&lastTxOut, block](AccountHistoryKey const & key, AccountHistoryValue const &) -> bool + auto shouldContinueToNextAccountHistory = [&lastTxOut, block](AccountHistoryKey const & key, CLazySerialize valueLazy) -> bool { if (key.owner != Params().GetConsensus().burnAddress) { return false; @@ -1868,9 +1869,7 @@ DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockI }; AccountHistoryKey startKey({Params().GetConsensus().burnAddress, static_cast(Params().GetConsensus().EunosHeight), std::numeric_limits::max()}); - pburnHistoryDB->ForEachAccountHistory(shouldContinueToNextAccountHistory, - Params().GetConsensus().burnAddress, - Params().GetConsensus().EunosHeight); + pburnHistoryDB->ForEachAccountHistory(shouldContinueToNextAccountHistory, startKey); for (uint32_t i = block.vtx.size(); i <= lastTxOut; ++i) { pburnHistoryDB->EraseAccountHistory({Params().GetConsensus().burnAddress, static_cast(Params().GetConsensus().EunosHeight), i}); @@ -2487,9 +2486,6 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl // Reset phanton TX to block TX count nPhantomBurnTx = block.vtx.size(); - // Reset phantom TX to end of block TX count - nPhantomAccTx = std::numeric_limits::max(); - // Wipe burn map, we only want TXs added during ConnectBlock mapBurnAmounts.clear(); @@ -4189,14 +4185,10 @@ static Res PoolSplits(CCustomCSView& view, CAmount& totalBalance, ATTRIBUTES& at for (auto& [owner, amount] : balancesToMigrate) { if (owner != Params().GetConsensus().burnAddress) { - CHistoryWriters subWriters{view.GetAccountHistoryStore(), nullptr, nullptr}; - CAccountsHistoryWriter subView(view, pindex->nHeight, GetNextAccPosition(), {}, uint8_t(CustomTxType::TokenSplit), &subWriters); - - res = subView.SubBalance(owner, CTokenAmount{oldPoolId, amount}); + res = view.SubBalance(owner, CTokenAmount{oldPoolId, amount}); if (!res.ok) { throw std::runtime_error(strprintf("SubBalance failed: %s", res.msg)); } - subView.Flush(); } if (oldPoolPair->totalLiquidity < CPoolPair::MINIMUM_LIQUIDITY) { @@ -4226,13 +4218,9 @@ static Res PoolSplits(CCustomCSView& view, CAmount& totalBalance, ATTRIBUTES& at totalBalance += amountB; } - CHistoryWriters addWriters{view.GetAccountHistoryStore(), nullptr, nullptr}; - CAccountsHistoryWriter addView(view, pindex->nHeight, GetNextAccPosition(), {}, uint8_t(CustomTxType::TokenSplit), &addWriters); - auto refundBalances = [&, owner = owner]() { - addView.AddBalance(owner, {newPoolPair.idTokenA, amountA}); - addView.AddBalance(owner, {newPoolPair.idTokenB, amountB}); - addView.Flush(); + view.AddBalance(owner, {newPoolPair.idTokenA, amountA}); + view.AddBalance(owner, {newPoolPair.idTokenB, amountB}); }; if (amountA <= 0 || amountB <= 0 || owner == Params().GetConsensus().burnAddress) { @@ -4273,13 +4261,11 @@ static Res PoolSplits(CCustomCSView& view, CAmount& totalBalance, ATTRIBUTES& at continue; } - res = addView.AddBalance(owner, {newPoolId, liquidity}); + res = view.AddBalance(owner, {newPoolId, liquidity}); if (!res) { - addView.Discard(); refundBalances(); continue; } - addView.Flush(); auto oldPoolLogStr = CTokenAmount{oldPoolId, amount}.ToString(); auto newPoolLogStr = CTokenAmount{newPoolId, liquidity}.ToString(); @@ -4419,18 +4405,6 @@ static Res VaultSplits(CCustomCSView& view, ATTRIBUTES& attributes, const DCT_ID if (!res) { return res; } - - if (view.GetVaultHistoryStore()) { - if (const auto vault = view.GetVault(vaultId)) { - VaultHistoryKey subKey{static_cast(height), vaultId, GetNextAccPosition(), vault->ownerAddress}; - VaultHistoryValue subValue{uint256{}, static_cast(CustomTxType::TokenSplit), {{oldTokenId, -amount}}}; - view.GetVaultHistoryStore()->WriteVaultHistory(subKey, subValue); - - VaultHistoryKey addKey{static_cast(height), vaultId, GetNextAccPosition(), vault->ownerAddress}; - VaultHistoryValue addValue{uint256{}, static_cast(CustomTxType::TokenSplit), {{newTokenId, newAmount}}}; - view.GetVaultHistoryStore()->WriteVaultHistory(addKey, addValue); - } - } } const auto loanToken = view.GetLoanTokenByID(newTokenId); @@ -4578,8 +4552,6 @@ void CChainState::ProcessTokenSplits(const CBlock& block, const CBlockIndex* pin } auto view{cache}; - view.SetAccountHistoryStore(); - view.SetVaultHistoryStore(); // Refund affected future swaps auto res = attributes->RefundFuturesContracts(view, std::numeric_limits::max(), id); @@ -4673,12 +4645,14 @@ void CChainState::ProcessTokenSplits(const CBlock& block, const CBlockIndex* pin continue; } - std::map> balanceUpdates; + CAccounts addAccounts; + CAccounts subAccounts; view.ForEachBalance([&, multiplier = multiplier](CScript const& owner, const CTokenAmount& balance) { if (oldTokenId.v == balance.nTokenId.v) { const auto newBalance = CalculateNewAmount(multiplier, balance.nValue); - balanceUpdates.emplace(owner, std::pair{{newTokenId, newBalance}, balance}); + addAccounts[owner].Add({newTokenId, newBalance}); + subAccounts[owner].Add(balance); totalBalance += newBalance; auto newBalanceStr = CTokenAmount{newTokenId, newBalance}.ToString(); @@ -4690,8 +4664,8 @@ void CChainState::ProcessTokenSplits(const CBlock& block, const CBlockIndex* pin }); LogPrintf("Token split info: rebalance " /* Continued */ - "(id: %d, symbol: %s, accounts: %d, val: %d)\n", - id, newToken.symbol, balanceUpdates.size(), totalBalance); + "(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); if (!res) { @@ -4700,26 +4674,18 @@ void CChainState::ProcessTokenSplits(const CBlock& block, const CBlockIndex* pin } try { - - for (const auto& [owner, balances] : balanceUpdates) { - - CHistoryWriters subWriters{view.GetAccountHistoryStore(), nullptr, nullptr}; - CAccountsHistoryWriter subView(view, pindex->nHeight, GetNextAccPosition(), {}, uint8_t(CustomTxType::TokenSplit), &subWriters); - - res = subView.SubBalance(owner, balances.second); + for (const auto& [owner, balances] : addAccounts) { + res = view.AddBalances(owner, balances); if (!res) { throw std::runtime_error(res.msg); } - subView.Flush(); - - CHistoryWriters addWriters{view.GetAccountHistoryStore(), nullptr, nullptr}; - CAccountsHistoryWriter addView(view, pindex->nHeight, GetNextAccPosition(), {}, uint8_t(CustomTxType::TokenSplit), &addWriters); + } - res = addView.AddBalance(owner, balances.first); + for (const auto& [owner, balances] : subAccounts) { + res = view.SubBalances(owner, balances); if (!res) { throw std::runtime_error(res.msg); } - addView.Flush(); } } catch (const std::runtime_error& e) { LogPrintf("Token split failed. %s\n", res.msg); @@ -4758,12 +4724,6 @@ void CChainState::ProcessTokenSplits(const CBlock& block, const CBlockIndex* pin } view.SetVariable(*attributes); view.Flush(); - if (auto accountHistory = view.GetAccountHistoryStore()) { - accountHistory->Flush(); - } - if (auto vaultHistory = view.GetVaultHistoryStore()) { - vaultHistory->Flush(); - } LogPrintf("Token split completed: (id: %d, mul: %d, time: %dms)\n", id, multiplier, GetTimeMillis() - time); } } diff --git a/test/functional/feature_burn_address.py b/test/functional/feature_burn_address.py index 3d68f1ebd34..bb2e45b48db 100755 --- a/test/functional/feature_burn_address.py +++ b/test/functional/feature_burn_address.py @@ -214,25 +214,14 @@ def run_test(self): assert_equal(len(result), 1) assert_equal(result[0]['type'], 'CreateToken') - # Get current block - current_block = self.nodes[0].getblockcount() - - # Revert all TXs + # Revert all TXs and check that burn history is empty self.nodes[0].invalidateblock(self.nodes[0].getblockhash(101)) - self.nodes[0].clearmempool() - - # Move the chain forward to the previous height - self.nodes[0].generate(current_block - self.nodes[0].getblockcount()) - - # Check that burn history is empty result = self.nodes[0].listburnhistory() assert_equal(len(result), 0) - # Check burn info reset result = self.nodes[0].getburninfo() - assert_equal(result['amount'], Decimal('0')) - assert_equal(result['feeburn'], Decimal('0')) - assert_equal(result['tokens'], []) + assert_equal(result['amount'], Decimal('0.0')) + assert_equal(len(result['tokens']), 0) if __name__ == '__main__': BurnAddressTest().main() diff --git a/test/functional/feature_futures.py b/test/functional/feature_futures.py index f9cb2805b40..4927a2a0215 100755 --- a/test/functional/feature_futures.py +++ b/test/functional/feature_futures.py @@ -854,7 +854,6 @@ def check_gov_var_change(self): txn_first = 4294967295 result = self.nodes[0].listaccounthistory('all', {"maxBlockHeight":self.nodes[0].getblockcount(), 'depth':0, 'txtype':'w'}) result.sort(key = sort_history, reverse = True) - assert_equal(len(result), 4) for result_entry in result: assert_equal(result_entry['blockHeight'], self.nodes[0].getblockcount()) assert_equal(result_entry['type'], 'FutureSwapRefund') diff --git a/test/functional/feature_token_fork.py b/test/functional/feature_token_fork.py index 171f857835a..5ccb848bc67 100755 --- a/test/functional/feature_token_fork.py +++ b/test/functional/feature_token_fork.py @@ -11,18 +11,21 @@ from test_framework.test_framework import DefiTestFramework from test_framework.authproxy import JSONRPCException -from test_framework.util import assert_equal, assert_raises_rpc_error +from test_framework.util import assert_equal class TokensForkTest (DefiTestFramework): def set_test_params(self): - self.num_nodes = 1 + self.num_nodes = 2 self.setup_clean_chain = True - self.extra_args = [['-txnotokens=0', '-amkheight=120']] + self.extra_args = [['-txnotokens=0', '-amkheight=120'], ['-txnotokens=0', '-amkheight=120']] def run_test(self): assert_equal(len(self.nodes[0].listtokens()), 1) # only one token == DFI - self.nodes[0].generate(102) + self.nodes[0].generate(100) + self.sync_blocks() + + self.nodes[0].generate(2) # for 2 matured utxos # Try to create token before AMK fork height but will fail: #======================== @@ -60,6 +63,7 @@ def run_test(self): # assert("Token tx before AMK" in errorString) self.nodes[0].generate(17) + self.sync_blocks() # Now at AMK height 120 # Now create again, it should pass @@ -67,39 +71,14 @@ def run_test(self): "symbol": "GOLD", "name": "shiny gold", "collateralAddress": collateralGold - }) - self.nodes[0].generate(1) - - txid = self.nodes[0].createtoken({ - "symbol": "SILVER", - "name": "just silver", - "collateralAddress": collateralSilver - }) - self.nodes[0].generate(1) - - # Get token ID - id_silver = list(self.nodes[0].gettoken('SILVER#129').keys())[0] - - # Check rollback of token - self.nodes[0].invalidateblock(self.nodes[0].getblockhash(self.nodes[0].getblockcount())) - self.nodes[0].clearmempool() - - # Make sure token not found - assert_raises_rpc_error(-5, "Token not found", self.nodes[0].gettoken, txid) - assert_raises_rpc_error(-5, "Token not found", self.nodes[0].gettoken, id_silver) - assert_raises_rpc_error(-5, "Token not found", self.nodes[0].gettoken, 'SILVER#129') - - # Create token again + }, []) self.nodes[0].createtoken({ "symbol": "SILVER", "name": "just silver", "collateralAddress": collateralSilver - }) - self.nodes[0].generate(1) - - # Check the same ID was provided, not an increment of the last one - assert_equal(id_silver, list(self.nodes[0].gettoken('SILVER#129').keys())[0]) + }, []) + self.nodes[0].generate(1) # After fork, create should pass, so now only have 3 kind of tokens tokens = self.nodes[0].listtokens() assert_equal(len(tokens), 3) @@ -114,6 +93,8 @@ def run_test(self): symbolGold = "GOLD#" + idGold symbolSilver = "SILVER#" + idSilver + self.sync_blocks() + # MINT: #======================== # Funding auth addresses @@ -123,9 +104,12 @@ def run_test(self): self.nodes[0].sendmany("", { collateralGold : 1, collateralSilver : 1 }) self.nodes[0].generate(1) + # print(self.nodes[0].listunspent()) + self.nodes[0].minttokens("300@" + symbolGold, []) self.nodes[0].minttokens("3000@" + symbolSilver, []) self.nodes[0].generate(1) + self.sync_blocks() # synthetically check for minting. restart w/o reindex and amk (so, token exists, but minting should fail) self.stop_node(0) @@ -137,5 +121,7 @@ def run_test(self): errorString = e.error['message'] assert("before AMK height" in errorString) + + if __name__ == '__main__': TokensForkTest ().main () diff --git a/test/functional/feature_token_split.py b/test/functional/feature_token_split.py index 0e12c64deb1..36dc52167e7 100755 --- a/test/functional/feature_token_split.py +++ b/test/functional/feature_token_split.py @@ -21,7 +21,7 @@ def set_test_params(self): self.setup_clean_chain = True self.fort_canning_crunch = 600 self.extra_args = [ - ['-vaultindex=1', '-txnotokens=0', '-amkheight=1', '-bayfrontheight=1', '-eunosheight=1', '-fortcanningheight=1', '-fortcanningmuseumheight=1', '-fortcanninghillheight=1', '-fortcanningroadheight=1', f'-fortcanningcrunchheight={self.fort_canning_crunch}', '-subsidytest=1']] + ['-txnotokens=0', '-amkheight=1', '-bayfrontheight=1', '-eunosheight=1', '-fortcanningheight=1', '-fortcanningmuseumheight=1', '-fortcanninghillheight=1', '-fortcanningroadheight=1', f'-fortcanningcrunchheight={self.fort_canning_crunch}', '-subsidytest=1']] def run_test(self): self.setup_test_tokens() @@ -295,35 +295,26 @@ def setup_test_vaults(self): self.nodes[0].utxostoaccount({self.address: f'30000@{self.symbolDFI}'}) self.nodes[0].generate(1) - for vault in range(100): + for _ in range(100): # Create vault vault_id = self.nodes[0].createvault(self.address, '') self.nodes[0].generate(1) - # Store single vault to check later - if vault == 0: - self.vault_id = vault_id - self.vault_loan = 0 - # Take 1 to 3 loans for _ in range(1, 4): # Deposit random collateral collateral = round(random.uniform(1, 100), 8) loan = truncate(str(collateral / 3), 8) - self.nodes[0].deposittovault(vault_id, self.address, f'{collateral}@{self.symbolDFI}') + self.nodes[0].deposittovault(vault_id, self.address, f'{str(collateral)}@{self.symbolDFI}') self.nodes[0].generate(1) # Take loan self.nodes[0].takeloan({ 'vaultId': vault_id, - 'amounts': f"{loan}@{self.symbolNVDA}" + 'amounts': f"{str(loan)}@{self.symbolNVDA}" }) self.nodes[0].generate(1) - # Store loan amounts - if vault == 0: - self.vault_loan += Decimal(loan) - def check_token_split(self, token_id, token_symbol, token_suffix, multiplier, minted, loan, collateral): # Check old token @@ -547,15 +538,11 @@ def token_split(self): assert_equal(result[f'v0/token/{self.idGOOGL}/loan_payback_fee_pct/{self.idTSLA}'], '0.25') assert_equal(result[f'v0/token/{self.idTSLA}/loan_payback_fee_pct/{self.idTSLA}'], '0.25') - # Check new balances and history + # Check new balances for [address, amount] in self.funded_addresses: account = self.nodes[0].getaccount(address) new_amount = Decimal(account[0].split('@')[0]) assert_equal(new_amount, amount * 2) - history = self.nodes[0].listaccounthistory(address, {'txtype': 'TokenSplit'}) - assert_equal(len(history), 2) - assert_equal(history[0]['amounts'][0], f'{-amount:.8f}' + f'@{self.symbolTSLA}/v1') - assert_equal(history[1]['amounts'][0], account[0]) # Token split self.nodes[0].setgov({"ATTRIBUTES":{f'v0/oracles/splits/{str(self.nodes[0].getblockcount() + 2)}':f'{self.idTSLA}/-3'}}) @@ -617,16 +604,6 @@ def pool_split(self): self.idGOOGL = list(self.nodes[0].gettoken(self.symbolGOOGL).keys())[0] self.idGD = list(self.nodes[0].gettoken(self.symbolGD).keys())[0] - # Check token split records in account history - balances = self.nodes[0].gettokenbalances({'start': int(self.idGD), 'including_start': True, 'limit': 1}, False, True) - result = self.nodes[0].listaccounthistory(self.address, {"maxBlockHeight":self.nodes[0].getblockcount(), 'txtype': 'TokenSplit', 'depth':0}) - assert_equal(result[0]['owner'], self.address) - assert_equal(result[0]['type'], 'TokenSplit') - assert_equal(result[0]['amounts'], [f'-{self.poolGDTotal - Decimal("0.00001")}@{self.symbolGD}/v1']) - assert_equal(result[1]['owner'], self.address) - assert_equal(result[1]['type'], 'TokenSplit') - assert_equal(result[1]['amounts'], balances) - # Token split self.nodes[0].setgov({"ATTRIBUTES":{f'v0/oracles/splits/{str(self.nodes[0].getblockcount() + 2)}':f'{self.idGOOGL}/-3'}}) self.nodes[0].generate(2) @@ -738,15 +715,6 @@ def vault_split(self): # Multiplier 2 self.execute_vault_split(self.idNVDA, self.symbolNVDA, 2, '/v1') - # Check split history - result = self.nodes[0].listvaulthistory(self.vault_id, {'maxBlockHeight': self.nodes[0].getblockcount(), 'depth':1}) - assert_equal(result[0]['address'], self.address) - assert_equal(result[0]['type'], 'TokenSplit') - assert_equal(result[0]['amounts'], [f'-{self.vault_loan}@{self.symbolNVDA}/v1']) - assert_equal(result[1]['address'], self.address) - assert_equal(result[1]['type'], 'TokenSplit') - assert_equal(result[1]['amounts'], [f'{self.vault_loan * 2}@{self.symbolNVDA}']) - # Swap old for new values self.idNVDA = list(self.nodes[0].gettoken(self.symbolNVDA).keys())[0] @@ -794,7 +762,7 @@ def check_govvar_deletion(self): def check_future_swap_refund(self): - # Set all futures attributes + # Set all futures attributes but set active to false self.nodes[0].setgov({"ATTRIBUTES":{ 'v0/params/dfip2203/reward_pct':'0.05', 'v0/params/dfip2203/block_period':'2880' @@ -810,9 +778,6 @@ def check_future_swap_refund(self): address_msft = self.nodes[0].getnewaddress("", "legacy") address_locked = self.nodes[0].getnewaddress("", "legacy") - # Get block to revert to - revert_block = self.nodes[0].getblockcount() - # Fund addresses self.nodes[0].accounttoaccount(self.address, {address_msft: [f'1@{self.symbolMSFT}', f'1@{self.symbolDUSD}']}) self.nodes[0].accounttoaccount(self.address, {address_locked: [f'1@{self.symbolMSFT}', f'1@{self.symbolDUSD}']}) @@ -852,37 +817,13 @@ def check_future_swap_refund(self): result = self.nodes[0].getaccount(address_msft) assert_equal(result, [f'1.00000000@{self.symbolDUSD}', f'2.00000000@{self.symbolMSFT}']) - # Check future swap and token split account history - result = self.nodes[0].listaccounthistory(address_msft, {"maxBlockHeight":self.nodes[0].getblockcount(), 'depth':0}) - assert_equal(result[0]['owner'], address_msft) - assert_equal(result[0]['type'], 'FutureSwapRefund') - assert_equal(result[0]['amounts'], [f'1.00000000@{self.symbolDUSD}']) - assert_equal(result[1]['owner'], address_msft) - assert_equal(result[1]['type'], 'FutureSwapRefund') - assert_equal(result[1]['amounts'], [f'1.00000000@{self.symbolMSFT}/v2']) - assert_equal(result[2]['owner'], address_msft) - assert_equal(result[2]['type'], 'TokenSplit') - assert_equal(result[2]['amounts'], [f'-1.00000000@{self.symbolMSFT}/v2']) - assert_equal(result[3]['owner'], address_msft) - assert_equal(result[3]['type'], 'TokenSplit') - assert_equal(result[3]['amounts'], [f'2.00000000@{self.symbolMSFT}']) - - # Get block to move forward to after revert - future_block = self.nodes[0].getblockcount() + # Swap old for new values + self.idMSFT = list(self.nodes[0].gettoken(self.symbolMSFT).keys())[0] - # Revert chain to before address funding - self.nodes[0].invalidateblock(self.nodes[0].getblockhash(revert_block)) - self.nodes[0].clearmempool() + # Unlock token + self.nodes[0].setgov({"ATTRIBUTES":{f'v0/locks/token/{self.idMSFT}':'false'}}) self.nodes[0].generate(1) - # Move forward again without split - self.nodes[0].generate(future_block - self.nodes[0].getblockcount()) - assert_equal(self.nodes[0].getblockcount(), future_block) - - # Account history should now be empty - result = self.nodes[0].listaccounthistory(address_msft, {"maxBlockHeight":self.nodes[0].getblockcount(), 'depth':0}) - assert_equal(result, []) - def migrate_auction_batches(self): # Create vaults diff --git a/test/functional/rpc_mn_basic.py b/test/functional/rpc_mn_basic.py index 6a13598e1f4..c0cdd842292 100755 --- a/test/functional/rpc_mn_basic.py +++ b/test/functional/rpc_mn_basic.py @@ -183,17 +183,6 @@ def run_test(self): mnAddress = self.nodes[0].getnewaddress("", "legacy") mnTx = self.nodes[0].createmasternode(mnAddress) self.nodes[0].generate(1) - - # Rollback masternode creation - self.nodes[0].invalidateblock(self.nodes[0].getblockhash(self.nodes[0].getblockcount())) - self.nodes[0].clearmempool() - - # Make sure MN not found - assert_raises_rpc_error(-5, "Masternode not found", self.nodes[0].getmasternode, mnTx) - - # Create masternode again - mnTx = self.nodes[0].createmasternode(mnAddress) - self.nodes[0].generate(1) assert_equal(self.nodes[0].listmasternodes({}, False)[mnTx], "PRE_ENABLED") # Try and resign masternode while still in PRE_ENABLED @@ -227,19 +216,7 @@ def run_test(self): self.nodes[0].resignmasternode(mnTx) self.nodes[0].generate(1) assert_equal(self.nodes[0].listmasternodes()[mnTx]['state'], "PRE_RESIGNED") - - # Test rollback of MN resign - self.nodes[0].invalidateblock(self.nodes[0].getblockhash(self.nodes[0].getblockcount())) - self.nodes[0].clearmempool() - - # Make sure rollback succesful - result = self.nodes[0].getmasternode(mnTx)[mnTx] - assert_equal(result['resignHeight'], -1) - assert_equal(result['resignTx'], '0000000000000000000000000000000000000000000000000000000000000000') - - # Resign again - self.nodes[0].resignmasternode(mnTx) - self.nodes[0].generate(40) + self.nodes[0].generate(39) assert_equal(self.nodes[0].listmasternodes()[mnTx]['state'], "PRE_RESIGNED") self.nodes[0].generate(1) assert_equal(self.nodes[0].listmasternodes()[mnTx]['state'], "RESIGNED") diff --git a/test/lint/lint-circular-dependencies.sh b/test/lint/lint-circular-dependencies.sh index 6910c2bb1f0..d97a284b253 100755 --- a/test/lint/lint-circular-dependencies.sh +++ b/test/lint/lint-circular-dependencies.sh @@ -28,8 +28,6 @@ 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 -> 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" "masternodes/anchors -> masternodes/masternodes -> masternodes/anchors" @@ -40,9 +38,8 @@ EXPECTED_CIRCULAR_DEPENDENCIES=( "masternodes/govvariables/attributes -> masternodes/gv -> masternodes/govvariables/attributes" "masternodes/govvariables/attributes -> masternodes/masternodes -> masternodes/govvariables/attributes" "masternodes/govvariables/attributes -> masternodes/masternodes -> masternodes/poolpairs -> masternodes/govvariables/attributes" + "masternodes/govvariables/attributes -> masternodes/masternodes -> validation -> masternodes/govvariables/attributes" "masternodes/govvariables/attributes -> masternodes/mn_checks -> masternodes/govvariables/attributes" - "masternodes/govvariables/attributes -> masternodes/mn_rpc -> masternodes/govvariables/attributes" - "masternodes/govvariables/attributes -> validation -> masternodes/govvariables/attributes" "masternodes/govvariables/icx_takerfee_per_btc -> masternodes/gv -> masternodes/govvariables/icx_takerfee_per_btc" "masternodes/govvariables/loan_daily_reward -> masternodes/gv -> masternodes/govvariables/loan_daily_reward" "masternodes/govvariables/loan_daily_reward -> masternodes/masternodes -> validation -> masternodes/govvariables/loan_daily_reward" @@ -58,8 +55,8 @@ EXPECTED_CIRCULAR_DEPENDENCIES=( "masternodes/govvariables/oracle_deviation -> masternodes/gv -> masternodes/govvariables/oracle_deviation" "masternodes/loan -> masternodes/masternodes -> masternodes/loan" "masternodes/masternodes -> masternodes/mn_checks -> masternodes/masternodes" + "masternodes/masternodes -> masternodes/mn_checks -> masternodes/vaulthistory -> masternodes/masternodes" "masternodes/masternodes -> masternodes/oracles -> masternodes/masternodes" - "masternodes/masternodes -> masternodes/vaulthistory -> masternodes/masternodes" "masternodes/masternodes -> net_processing -> masternodes/masternodes" "masternodes/masternodes -> validation -> masternodes/masternodes" "masternodes/masternodes -> wallet/wallet -> masternodes/masternodes"