diff --git a/src/masternodes/mn_checks.cpp b/src/masternodes/mn_checks.cpp index 6e96c4ca3d..c99e12add3 100644 --- a/src/masternodes/mn_checks.cpp +++ b/src/masternodes/mn_checks.cpp @@ -304,64 +304,43 @@ CCustomTxMessage customTypeToMessage(CustomTxType txType) { extern std::string ScriptToString(const CScript &script); +template +constexpr bool FalseType = false; + +template +constexpr bool IsOneOf() { + return false; +} + +template +constexpr bool IsOneOf() { + return std::is_same_v || IsOneOf(); +} + class CCustomMetadataParseVisitor { uint32_t height; const Consensus::Params &consensus; const std::vector &metadata; - Res isPostAMKFork() const { - Require(static_cast(height) >= consensus.AMKHeight, "called before AMK height"); - return Res::Ok(); - } - - Res isPostBayfrontFork() const { - Require(static_cast(height) >= consensus.BayfrontHeight, "called before Bayfront height"); - return Res::Ok(); - } - - Res isPostBayfrontGardensFork() const { - Require(static_cast(height) >= consensus.BayfrontGardensHeight, "called before Bayfront Gardens height"); - return Res::Ok(); - } - - Res isPostEunosFork() const { - Require(static_cast(height) >= consensus.EunosHeight, "called before Eunos height"); - return Res::Ok(); - } - - Res isPostFortCanningFork() const { - Require(static_cast(height) >= consensus.FortCanningHeight, "called before FortCanning height"); - return Res::Ok(); - } - - Res isPostFortCanningHillFork() const { - Require(static_cast(height) >= consensus.FortCanningHillHeight, "called before FortCanningHill height"); - return Res::Ok(); - } - - Res isPostFortCanningRoadFork() const { - Require(static_cast(height) >= consensus.FortCanningRoadHeight, "called before FortCanningRoad height"); - return Res::Ok(); - } - - Res isPostFortCanningEpilogueFork() const { - Require(static_cast(height) >= consensus.FortCanningEpilogueHeight, - "called before FortCanningEpilogue height"); - return Res::Ok(); - } - - Res isPostGrandCentralFork() const { - if (static_cast(height) < consensus.GrandCentralHeight) { - return Res::Err("called before GrandCentral height"); + Res IsHardforkEnabled(int startHeight) const { + const std::unordered_map hardforks = { + { consensus.AMKHeight, "called before AMK height" }, + { consensus.BayfrontHeight, "called before Bayfront height" }, + { consensus.BayfrontGardensHeight, "called before Bayfront Gardens height" }, + { consensus.EunosHeight, "called before Eunos height" }, + { consensus.EunosPayaHeight, "called before EunosPaya height" }, + { consensus.FortCanningHeight, "called before FortCanning height" }, + { consensus.FortCanningHillHeight, "called before FortCanningHill height" }, + { consensus.FortCanningRoadHeight, "called before FortCanningRoad height" }, + { consensus.FortCanningEpilogueHeight, "called before FortCanningEpilogue height" }, + { consensus.GrandCentralHeight, "called before GrandCentral height" }, + }; + if (startHeight && int(height) < startHeight) { + auto it = hardforks.find(startHeight); + assert(it != hardforks.end()); + return Res::Err(it->second); } - return Res::Ok(); - } - template - Res serialize(T &obj) const { - CDataStream ss(metadata, SER_NETWORK, PROTOCOL_VERSION); - ss >> obj; - Require(ss.empty(), "deserialization failed: excess %d bytes", ss.size()); return Res::Ok(); } @@ -373,306 +352,110 @@ class CCustomMetadataParseVisitor { consensus(consensus), metadata(metadata) {} - Res operator()(CCreateMasterNodeMessage &obj) const { return serialize(obj); } - - Res operator()(CResignMasterNodeMessage &obj) const { - Require(metadata.size() == sizeof(obj), "metadata must contain 32 bytes"); - return serialize(obj); - } - - Res operator()(CUpdateMasterNodeMessage &obj) const { - Require(isPostGrandCentralFork()); - return serialize(obj); - } - - Res operator()(CCreateTokenMessage &obj) const { - Require(isPostAMKFork()); - return serialize(obj); - } - - Res operator()(CUpdateTokenPreAMKMessage &obj) const { - Require(isPostAMKFork()); - Require(!isPostBayfrontFork(), "called post Bayfront height"); - return serialize(obj); - } - - Res operator()(CUpdateTokenMessage &obj) const { - Require(isPostBayfrontFork()); - return serialize(obj); - } - - Res operator()(CMintTokensMessage &obj) const { - Require(isPostAMKFork()); - return serialize(obj); - } - - Res operator()(CPoolSwapMessage &obj) const { - Require(isPostBayfrontFork()); - return serialize(obj); - } - - Res operator()(CLiquidityMessage &obj) const { - Require(isPostBayfrontFork()); - return serialize(obj); - } - - Res operator()(CRemoveLiquidityMessage &obj) const { - Require(isPostBayfrontFork()); - return serialize(obj); - } - - Res operator()(CUtxosToAccountMessage &obj) const { - Require(isPostAMKFork()); - return serialize(obj); - } - - Res operator()(CAccountToUtxosMessage &obj) const { - Require(isPostAMKFork()); - return serialize(obj); - } - - Res operator()(CAccountToAccountMessage &obj) const { - Require(isPostAMKFork()); - return serialize(obj); - } - - Res operator()(CAnyAccountsToAccountsMessage &obj) const { - Require(isPostBayfrontGardensFork()); - return serialize(obj); - } - - Res operator()(CSmartContractMessage &obj) const { - Require(isPostFortCanningHillFork()); - return serialize(obj); - } - - Res operator()(CFutureSwapMessage &obj) const { - Require(isPostFortCanningRoadFork()); - return serialize(obj); + template + Res EnabledAfter() const { + if constexpr (IsOneOf()) + return IsHardforkEnabled(consensus.AMKHeight); + else if constexpr (IsOneOf()) + return IsHardforkEnabled(consensus.BayfrontHeight); + else if constexpr (IsOneOf()) + return IsHardforkEnabled(consensus.EunosHeight); + else if constexpr (IsOneOf()) + return IsHardforkEnabled(consensus.FortCanningHeight); + else if constexpr (IsOneOf()) + return IsHardforkEnabled(consensus.BayfrontGardensHeight); + else if constexpr (IsOneOf()) + return IsHardforkEnabled(consensus.FortCanningHillHeight); + else if constexpr (IsOneOf()) + return IsHardforkEnabled(consensus.FortCanningRoadHeight); + else if constexpr (IsOneOf()) + return IsHardforkEnabled(consensus.FortCanningEpilogueHeight); + else if constexpr (IsOneOf()) + return IsHardforkEnabled(consensus.GrandCentralHeight); + else if constexpr (IsOneOf()) + return Res::Ok(); + else + static_assert(FalseType, "Unhandled type"); } - Res operator()(CCreatePoolPairMessage &obj) const { - Require(isPostBayfrontFork()); - - CDataStream ss(metadata, SER_NETWORK, PROTOCOL_VERSION); - ss >> obj.poolPair; - ss >> obj.pairSymbol; + template + Res DisabledAfter() const { + if constexpr (IsOneOf()) + return IsHardforkEnabled(consensus.BayfrontHeight) ? Res::Err("called after Bayfront height") : Res::Ok(); - // Read custom pool rewards - if (static_cast(height) >= consensus.ClarkeQuayHeight && !ss.empty()) { - ss >> obj.rewards; - } - Require(ss.empty(), "deserialization failed: excess %d bytes", ss.size()); return Res::Ok(); } - Res operator()(CUpdatePoolPairMessage &obj) const { - Require(isPostBayfrontFork()); - - CDataStream ss(metadata, SER_NETWORK, PROTOCOL_VERSION); - // serialize poolId as raw integer - ss >> obj.poolId.v; - ss >> obj.status; - ss >> obj.commission; - ss >> obj.ownerAddress; - - // Read custom pool rewards - if (static_cast(height) >= consensus.ClarkeQuayHeight && !ss.empty()) { - ss >> obj.rewards; - } - - Require(ss.empty(), "deserialization failed: excess %d bytes", ss.size()); - return Res::Ok(); - } + template + Res operator()(T& obj) const { + auto res = EnabledAfter(); + if (!res) + return res; - Res operator()(CGovernanceMessage &obj) const { - Require(isPostBayfrontFork()); + res = DisabledAfter(); + if (!res) + return res; - std::string name; CDataStream ss(metadata, SER_NETWORK, PROTOCOL_VERSION); - while (!ss.empty()) { - ss >> name; - auto var = GovVariable::Create(name); - Require(var, "'%s': variable is not registered", name); - ss >> *var; - obj.govs.insert(std::move(var)); - } - return Res::Ok(); - } - - Res operator()(CGovernanceHeightMessage &obj) const { - Require(isPostFortCanningFork()); + ss >> obj; + if (!ss.empty()) + return Res::Err("deserialization failed: excess %d bytes", ss.size()); - CDataStream ss(metadata, SER_NETWORK, PROTOCOL_VERSION); - std::string name; - ss >> name; - obj.govVar = GovVariable::Create(name); - Require(obj.govVar, "'%s': variable is not registered", name); - ss >> *obj.govVar; - ss >> obj.startHeight; return Res::Ok(); } - Res operator()(CAppointOracleMessage &obj) const { - Require(isPostEunosFork()); - return serialize(obj); - } - - Res operator()(CRemoveOracleAppointMessage &obj) const { - Require(isPostEunosFork()); - return serialize(obj); - } - - Res operator()(CUpdateOracleAppointMessage &obj) const { - Require(isPostEunosFork()); - return serialize(obj); - } - - Res operator()(CSetOracleDataMessage &obj) const { - Require(isPostEunosFork()); - return serialize(obj); - } - - Res operator()(CICXCreateOrderMessage &obj) const { - Require(isPostEunosFork()); - return serialize(obj); - } - - Res operator()(CICXMakeOfferMessage &obj) const { - Require(isPostEunosFork()); - return serialize(obj); - } - - Res operator()(CICXSubmitDFCHTLCMessage &obj) const { - Require(isPostEunosFork()); - return serialize(obj); - } - - Res operator()(CICXSubmitEXTHTLCMessage &obj) const { - Require(isPostEunosFork()); - return serialize(obj); - } - - Res operator()(CICXClaimDFCHTLCMessage &obj) const { - Require(isPostEunosFork()); - return serialize(obj); - } - - Res operator()(CICXCloseOrderMessage &obj) const { - Require(isPostEunosFork()); - return serialize(obj); - } - - Res operator()(CICXCloseOfferMessage &obj) const { - Require(isPostEunosFork()); - return serialize(obj); - } - - Res operator()(CPoolSwapMessageV2 &obj) const { - Require(isPostFortCanningFork()); - return serialize(obj); - } - - Res operator()(CLoanSetCollateralTokenMessage &obj) const { - Require(isPostFortCanningFork()); - return serialize(obj); - } - - Res operator()(CLoanSetLoanTokenMessage &obj) const { - Require(isPostFortCanningFork()); - return serialize(obj); - } - - Res operator()(CLoanUpdateLoanTokenMessage &obj) const { - Require(isPostFortCanningFork()); - return serialize(obj); - } - - Res operator()(CLoanSchemeMessage &obj) const { - Require(isPostFortCanningFork()); - return serialize(obj); - } - - Res operator()(CDefaultLoanSchemeMessage &obj) const { - Require(isPostFortCanningFork()); - return serialize(obj); - } - - Res operator()(CDestroyLoanSchemeMessage &obj) const { - Require(isPostFortCanningFork()); - return serialize(obj); - } - - Res operator()(CVaultMessage &obj) const { - Require(isPostFortCanningFork()); - return serialize(obj); - } - - Res operator()(CCloseVaultMessage &obj) const { - Require(isPostFortCanningFork()); - return serialize(obj); - } - - Res operator()(CUpdateVaultMessage &obj) const { - Require(isPostFortCanningFork()); - return serialize(obj); - } - - Res operator()(CDepositToVaultMessage &obj) const { - Require(isPostFortCanningFork()); - return serialize(obj); - } - - Res operator()(CWithdrawFromVaultMessage &obj) const { - Require(isPostFortCanningFork()); - return serialize(obj); - } - - Res operator()(CPaybackWithCollateralMessage &obj) const { - Require(isPostFortCanningEpilogueFork()); - return serialize(obj); - } - - Res operator()(CLoanTakeLoanMessage &obj) const { - Require(isPostFortCanningFork()); - return serialize(obj); - } - - Res operator()(CLoanPaybackLoanMessage &obj) const { - Require(isPostFortCanningFork()); - return serialize(obj); - } - - Res operator()(CLoanPaybackLoanV2Message &obj) const { - Require(isPostFortCanningRoadFork()); - return serialize(obj); - } - - Res operator()(CAuctionBidMessage &obj) const { - Require(isPostFortCanningFork()); - return serialize(obj); - } - - Res operator()(CBurnTokensMessage &obj) const { - Require(isPostGrandCentralFork()); - return serialize(obj); - } - - Res operator()(CCreatePropMessage &obj) const { - Require(isPostGrandCentralFork()); - return serialize(obj); - } - - Res operator()(CPropVoteMessage &obj) const { - Require(isPostGrandCentralFork()); - return serialize(obj); - } - - Res operator()(CGovernanceUnsetMessage &obj) const { - Require(isPostGrandCentralFork()); - return serialize(obj); - } - Res operator()(CCustomTxMessageNone &) const { return Res::Ok(); } }; @@ -1549,14 +1332,15 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { Res operator()(const CCreatePoolPairMessage &obj) const { // check foundation auth Require(HasFoundationAuth()); - Require(obj.poolPair.commission >= 0 && obj.poolPair.commission <= COIN, "wrong commission"); + Require(obj.commission >= 0 && obj.commission <= COIN, "wrong commission"); if (height >= static_cast(Params().GetConsensus().FortCanningCrunchHeight)) { Require(obj.pairSymbol.find('/') == std::string::npos, "token symbol should not contain '/'"); } /// @todo ownerAddress validity checked only in rpc. is it enough? - CPoolPair poolPair(obj.poolPair); + CPoolPair poolPair{}; + static_cast(poolPair) = obj; auto pairSymbol = obj.pairSymbol; poolPair.creationTx = tx.GetHash(); poolPair.creationHeight = height; @@ -1986,9 +1770,12 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { // check foundation auth Require(HasFoundationAuth()); for (const auto &gov : obj.govs) { - Res res{}; + if (!gov.second) { + return Res::Err("'%s': variable does not registered", gov.first); + } - auto var = gov; + auto var = gov.second; + Res res{}; if (var->GetName() == "ATTRIBUTES") { // Add to existing ATTRIBUTES instead of overwriting. @@ -2065,7 +1852,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { const auto diff = height % mnview.GetIntervalBlock(); if (diff != 0) { // Store as pending change - storeGovVars({var, height + mnview.GetIntervalBlock() - diff}, mnview); + storeGovVars({gov.first, var, height + mnview.GetIntervalBlock() - diff}, mnview); continue; } } diff --git a/src/masternodes/mn_checks.h b/src/masternodes/mn_checks.h index 140d973233..4435d32be8 100644 --- a/src/masternodes/mn_checks.h +++ b/src/masternodes/mn_checks.h @@ -346,27 +346,40 @@ struct CBurnTokensMessage { } }; -struct CCreatePoolPairMessage { - CPoolPairMessage poolPair; - std::string pairSymbol; - CBalances rewards; -}; - -struct CUpdatePoolPairMessage { - DCT_ID poolId; - bool status; - CAmount commission; - CScript ownerAddress; - CBalances rewards; -}; - struct CGovernanceMessage { - std::set> govs; + std::unordered_map> govs; + + ADD_SERIALIZE_METHODS; + template + inline void SerializationOp(Stream& s, Operation ser_action) { + std::string name; + while(!s.empty()) { + s >> name; + auto& gov = govs[name]; + auto var = GovVariable::Create(name); + if (!var) break; + s >> *var; + gov = std::move(var); + } + } }; struct CGovernanceHeightMessage { + std::string govName; std::shared_ptr govVar; uint32_t startHeight; + + ADD_SERIALIZE_METHODS; + template + inline void SerializationOp(Stream& s, Operation ser_action) { + if (!s.empty()) { + s >> govName; + if ((govVar = GovVariable::Create(govName))) { + s >> *govVar; + s >> startHeight; + } + } + } }; struct CGovernanceUnsetMessage { diff --git a/src/masternodes/poolpairs.h b/src/masternodes/poolpairs.h index bb52a90dda..b7d2b586ae 100644 --- a/src/masternodes/poolpairs.h +++ b/src/masternodes/poolpairs.h @@ -80,7 +80,7 @@ struct CPoolSwapMessageV2 { } }; -struct CPoolPairMessage { +struct CPoolPairMessageBase { DCT_ID idTokenA, idTokenB; CAmount commission; // comission %% for traders CScript ownerAddress; @@ -98,14 +98,44 @@ struct CPoolPairMessage { } }; -class CPoolPair : public CPoolPairMessage { +struct CCreatePoolPairMessage : public CPoolPairMessageBase { + std::string pairSymbol; + CBalances rewards; + + ADD_SERIALIZE_METHODS; + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITEAS(CPoolPairMessageBase, *this); + READWRITE(pairSymbol); + if (!s.empty()) + READWRITE(rewards); + } +}; + +struct CUpdatePoolPairMessage { + DCT_ID poolId; + bool status; + CAmount commission; + CScript ownerAddress; + CBalances rewards; + + ADD_SERIALIZE_METHODS; + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(poolId.v); + READWRITE(status); + READWRITE(commission); + READWRITE(ownerAddress); + if (!s.empty()) + READWRITE(rewards); + } +}; + +class CPoolPair : public CPoolPairMessageBase { public: static const CAmount MINIMUM_LIQUIDITY = 1000; static const CAmount SLOPE_SWAP_RATE = 1000; static const uint32_t PRECISION = (uint32_t)COIN; // or just PRECISION_BITS for "<<" and ">>" - CPoolPair(const CPoolPairMessage &msg = {}) - : CPoolPairMessage(msg) {} - virtual ~CPoolPair() = default; // temporary values, not serialized CAmount reserveA = 0; @@ -158,7 +188,7 @@ class CPoolPair : public CPoolPairMessage { if (!ser_action.ForRead()) ioProofer(); - READWRITEAS(CPoolPairMessage, *this); + READWRITEAS(CPoolPairMessageBase, *this); READWRITE(rewards); READWRITE(creationTx); READWRITE(creationHeight); diff --git a/src/masternodes/rpc_customtx.cpp b/src/masternodes/rpc_customtx.cpp index eb69f1ab79..a743323557 100644 --- a/src/masternodes/rpc_customtx.cpp +++ b/src/masternodes/rpc_customtx.cpp @@ -198,13 +198,13 @@ class CCustomTxRpcVisitor { rpcInfo.pushKV("creationTx", tx.GetHash().GetHex()); if (auto tokenPair = mnview.GetTokenByCreationTx(tx.GetHash())) tokenInfo(tokenPair->second); - if (auto tokenA = mnview.GetToken(obj.poolPair.idTokenA)) + if (auto tokenA = mnview.GetToken(obj.idTokenA)) rpcInfo.pushKV("tokenA", tokenA->name); - if (auto tokenB = mnview.GetToken(obj.poolPair.idTokenB)) + if (auto tokenB = mnview.GetToken(obj.idTokenB)) rpcInfo.pushKV("tokenB", tokenB->name); - rpcInfo.pushKV("commission", ValueFromAmount(obj.poolPair.commission)); - rpcInfo.pushKV("status", obj.poolPair.status); - rpcInfo.pushKV("ownerAddress", ScriptToString(obj.poolPair.ownerAddress)); + rpcInfo.pushKV("commission", ValueFromAmount(obj.commission)); + rpcInfo.pushKV("status", obj.status); + rpcInfo.pushKV("ownerAddress", ScriptToString(obj.ownerAddress)); customRewardsInfo(obj.rewards); } @@ -254,7 +254,8 @@ class CCustomTxRpcVisitor { } void operator()(const CGovernanceMessage &obj) const { - for (const auto &var : obj.govs) { + for (const auto &gov : obj.govs) { + auto& var = gov.second; rpcInfo.pushKV(var->GetName(), var->Export()); } } diff --git a/src/masternodes/rpc_poolpair.cpp b/src/masternodes/rpc_poolpair.cpp index 83453c640d..5b735f6f88 100644 --- a/src/masternodes/rpc_poolpair.cpp +++ b/src/masternodes/rpc_poolpair.cpp @@ -660,20 +660,22 @@ UniValue createpoolpair(const JSONRPCRequest &request) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("pairSymbol is larger than %d", symbolLength)); } - CPoolPairMessage poolPairMsg; + CCreatePoolPairMessage poolPairMsg; poolPairMsg.idTokenA = idtokenA; poolPairMsg.idTokenB = idtokenB; poolPairMsg.commission = commission; poolPairMsg.status = status; poolPairMsg.ownerAddress = ownerAddress; - - CDataStream metadata(DfTxMarker, SER_NETWORK, PROTOCOL_VERSION); - metadata << static_cast(CustomTxType::CreatePoolPair) << poolPairMsg << pairSymbol; + poolPairMsg.pairSymbol = pairSymbol; if (targetHeight >= Params().GetConsensus().ClarkeQuayHeight) { - metadata << rewards; + poolPairMsg.rewards = rewards; } + CDataStream metadata(DfTxMarker, SER_NETWORK, PROTOCOL_VERSION); + metadata << static_cast(CustomTxType::CreatePoolPair) + << poolPairMsg; + CScript scriptMeta; scriptMeta << OP_RETURN << ToByteVector(metadata);