From a439e98408e270acea5e371257b2c3b8bc6ed630 Mon Sep 17 00:00:00 2001 From: gladcow Date: Tue, 4 Jul 2017 20:31:57 +0300 Subject: [PATCH] Replace watchdogs with ping (#1491) * Add hassentinelping to governanceinfo * sentinelping rpc call * additional fields in mnp * sentinel ping implementation * change sentinel state to byte in mnp * use adjusted time in sentinel ping * update nTimeLastWatchdogVote if sentinel ping is actual * remove unused fields * bump protocol to 70207 * Fix small issues - fix the error message text in CActivbeMasternodeUpdateSentinelPing; - add empty string before public: in CActiveMasternode class declaration; - rename field sentinelPing in CMasternodePing to sentinelIsActual and change $ - decrease sentinelVersion field size to uint16_t; * revert proto bump for MIN_... consts * revert changes in getgovernanceinfo * Update mn vote time for remote masternodes - call UpdateWatchdogVoteTime in CMasternodeMan::ProcessMessage - deserialize masternodeping from the previous version archive without exception - add ability to set time in UpdateWatchdogVoteTime - set nTimeLastWatchdogVote to masternode ping sigTime if sentinel is actual - bump CMasternodeMan::SERIALIZATION_VERSION_STRING * remove mn state checks and add correct rpc param convertion * fix var names * Helper class for version in string and integer form * String version in sentinel ping Version format is "x.x.x" * test for bacward compatibility in serialization * Change VersionInfo class to convert functions --- src/activemasternode.cpp | 11 ++++++++ src/activemasternode.h | 7 ++++++ src/masternode.cpp | 10 +++++--- src/masternode.h | 15 +++++++++-- src/masternodeman.cpp | 13 +++++++++- src/rpc/governance.cpp | 1 + src/rpc/masternode.cpp | 20 +++++++++++++++ src/rpc/server.cpp | 1 + src/rpc/server.h | 1 + src/test/rpc_tests.cpp | 7 ++++++ src/test/serialize_tests.cpp | 49 ++++++++++++++++++++++++++++++++++++ src/test/util_tests.cpp | 15 +++++++++++ src/util.cpp | 38 ++++++++++++++++++++++++++++ src/util.h | 19 ++++++++++++++ src/version.h | 2 +- 15 files changed, 202 insertions(+), 7 deletions(-) diff --git a/src/activemasternode.cpp b/src/activemasternode.cpp index 48b639245..758a2fffd 100644 --- a/src/activemasternode.cpp +++ b/src/activemasternode.cpp @@ -108,6 +108,9 @@ bool CActiveMasternode::SendMasternodePing() } CMasternodePing mnp(vin); + mnp.nSentinelVersion = nSentinelVersion; + mnp.fSentinelIsCurrent = + (abs(GetAdjustedTime() - nSentinelPingTime) < MASTERNODE_WATCHDOG_MAX_SECONDS); if(!mnp.Sign(keyMasternode, pubKeyMasternode)) { LogPrintf("CActiveMasternode::SendMasternodePing -- ERROR: Couldn't sign Masternode Ping\n"); return false; @@ -127,6 +130,14 @@ bool CActiveMasternode::SendMasternodePing() return true; } +bool CActiveMasternode::UpdateSentinelPing(int version) +{ + nSentinelVersion = version; + nSentinelPingTime = GetAdjustedTime(); + + return true; +} + void CActiveMasternode::ManageStateInitial() { LogPrint("masternode", "CActiveMasternode::ManageStateInitial -- status = %s, type = %s, pinger enabled = %d\n", GetStatus(), GetTypeString(), fPingerEnabled); diff --git a/src/activemasternode.h b/src/activemasternode.h index 3c962b06a..0c5058446 100644 --- a/src/activemasternode.h +++ b/src/activemasternode.h @@ -40,6 +40,10 @@ private: /// Ping Masternode bool SendMasternodePing(); + // sentinel ping data + int64_t nSentinelPingTime; + uint32_t nSentinelVersion; + public: // Keys for the active Masternode CPubKey pubKeyMasternode; @@ -52,6 +56,7 @@ public: int nState; // should be one of ACTIVE_MASTERNODE_XXXX std::string strNotCapableReason; + CActiveMasternode() : eType(MASTERNODE_UNKNOWN), fPingerEnabled(false), @@ -69,6 +74,8 @@ public: std::string GetStatus() const; std::string GetTypeString() const; + bool UpdateSentinelPing(int version); + private: void ManageStateInitial(); void ManageStateRemote(); diff --git a/src/masternode.cpp b/src/masternode.cpp index c2a9ef661..e2b81f2d3 100644 --- a/src/masternode.cpp +++ b/src/masternode.cpp @@ -735,7 +735,9 @@ void CMasternodeBroadcast::Relay() RelayInv(inv); } -CMasternodePing::CMasternodePing(CTxIn& vinNew) +CMasternodePing::CMasternodePing(CTxIn& vinNew) : + fSentinelIsCurrent(false), + nSentinelVersion(0) { LOCK(cs_main); if (!chainActive.Tip() || chainActive.Height() < 12) return; @@ -751,6 +753,7 @@ bool CMasternodePing::Sign(CKey& keyMasternode, CPubKey& pubKeyMasternode) std::string strError; std::string strMasterNodeSignMessage; + // TODO: add sentinel data sigTime = GetAdjustedTime(); std::string strMessage = vin.ToString() + blockHash.ToString() + boost::lexical_cast(sigTime); @@ -769,6 +772,7 @@ bool CMasternodePing::Sign(CKey& keyMasternode, CPubKey& pubKeyMasternode) bool CMasternodePing::CheckSignature(CPubKey& pubKeyMasternode, int &nDos) { + // TODO: add sentinel data std::string strMessage = vin.ToString() + blockHash.ToString() + boost::lexical_cast(sigTime); std::string strError = ""; nDos = 0; @@ -909,10 +913,10 @@ void CMasternode::RemoveGovernanceObject(uint256 nGovernanceObjectHash) mapGovernanceObjectsVotedOn.erase(it); } -void CMasternode::UpdateWatchdogVoteTime() +void CMasternode::UpdateWatchdogVoteTime(uint64_t nVoteTime) { LOCK(cs); - nTimeLastWatchdogVote = GetTime(); + nTimeLastWatchdogVote = (nVoteTime == 0) ? GetTime() : nVoteTime; } /** diff --git a/src/masternode.h b/src/masternode.h index bd7cbde6f..91a138f01 100644 --- a/src/masternode.h +++ b/src/masternode.h @@ -23,6 +23,7 @@ static const int MASTERNODE_WATCHDOG_MAX_SECONDS = 120 * 60; static const int MASTERNODE_NEW_START_REQUIRED_SECONDS = 180 * 60; static const int MASTERNODE_POSE_BAN_MAX_SCORE = 5; + // // The Masternode Ping Class : Contains a different serialize method for sending pings from masternodes throughout the network // @@ -34,13 +35,17 @@ public: uint256 blockHash; int64_t sigTime; //mnb message times std::vector vchSig; + bool fSentinelIsCurrent; // true if last sentinel ping was actual + uint32_t nSentinelVersion; // MSB is always 0, other 3 bits corresponds to x.x.x version scheme //removed stop CMasternodePing() : vin(), blockHash(), sigTime(0), - vchSig() + vchSig(), + fSentinelIsCurrent(false), + nSentinelVersion(0) {} CMasternodePing(CTxIn& vinNew); @@ -53,6 +58,10 @@ public: READWRITE(blockHash); READWRITE(sigTime); READWRITE(vchSig); + if(ser_action.ForRead() && (s.size() == 0)) + return; + READWRITE(fSentinelIsCurrent); + READWRITE(nSentinelVersion); } void swap(CMasternodePing& first, CMasternodePing& second) // nothrow @@ -66,6 +75,8 @@ public: swap(first.blockHash, second.blockHash); swap(first.sigTime, second.sigTime); swap(first.vchSig, second.vchSig); + swap(first.fSentinelIsCurrent, second.fSentinelIsCurrent); + swap(first.nSentinelVersion, second.nSentinelVersion); } uint256 GetHash() const @@ -318,7 +329,7 @@ public: void RemoveGovernanceObject(uint256 nGovernanceObjectHash); - void UpdateWatchdogVoteTime(); + void UpdateWatchdogVoteTime(uint64_t nVoteTime = 0); CMasternode& operator=(CMasternode from) { diff --git a/src/masternodeman.cpp b/src/masternodeman.cpp index e84b96c8a..aaba446a7 100644 --- a/src/masternodeman.cpp +++ b/src/masternodeman.cpp @@ -16,7 +16,7 @@ /** Masternode manager */ CMasternodeMan mnodeman; -const std::string CMasternodeMan::SERIALIZATION_VERSION_STRING = "CMasternodeMan-Version-4"; +const std::string CMasternodeMan::SERIALIZATION_VERSION_STRING = "CMasternodeMan-Version-5"; struct CompareLastPaidBlock { @@ -849,6 +849,12 @@ void CMasternodeMan::ProcessMessage(CNode* pfrom, std::string& strCommand, CData // see if we have this Masternode CMasternode* pmn = mnodeman.Find(mnp.vin); + // if masternode uses sentinel ping instead of watchdog + // we shoud update nTimeLastWatchdogVote here if sentinel + // ping flag is actual + if(pmn && mnp.fSentinelIsCurrent) + pmn->UpdateWatchdogVoteTime(mnp.sigTime); + // too late, new MNANNOUNCE is required if(pmn && pmn->IsNewStartRequired()) return; @@ -1642,6 +1648,11 @@ void CMasternodeMan::SetMasternodeLastPing(const CTxIn& vin, const CMasternodePi return; } pMN->lastPing = mnp; + // if masternode uses sentinel ping instead of watchdog + // we shoud update nTimeLastWatchdogVote here if sentinel + // ping flag is actual + if(mnp.fSentinelIsCurrent) + pMN->UpdateWatchdogVoteTime(mnp.sigTime); mapSeenMasternodePing.insert(std::make_pair(mnp.GetHash(), mnp)); CMasternodeBroadcast mnb(*pMN); diff --git a/src/rpc/governance.cpp b/src/rpc/governance.cpp index 52dee66d4..674a326b8 100644 --- a/src/rpc/governance.cpp +++ b/src/rpc/governance.cpp @@ -972,3 +972,4 @@ UniValue getsuperblockbudget(const UniValue& params, bool fHelp) return strBudget; } + diff --git a/src/rpc/masternode.cpp b/src/rpc/masternode.cpp index 1d44cda61..9199fab65 100644 --- a/src/rpc/masternode.cpp +++ b/src/rpc/masternode.cpp @@ -795,3 +795,23 @@ UniValue masternodebroadcast(const UniValue& params, bool fHelp) return NullUniValue; } + +UniValue sentinelping(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) { + throw std::runtime_error( + "sentinelping version\n" + "\nSentinel ping.\n" + "\nArguments:\n" + "1. version (string, required) Sentinel version in the form \"x.x.x\"\n" + "\nResult:\n" + "state (boolean) Ping result\n" + "\nExamples:\n" + + HelpExampleCli("sentinelping", "1.0.2") + + HelpExampleRpc("sentinelping", "1.0.2") + ); + } + + activeMasternode.UpdateSentinelPing(StringVersionToInt(params[0].get_str())); + return true; +} diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 0059294bb..3745aede6 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -353,6 +353,7 @@ static const CRPCCommand vRPCCommands[] = { "dash", "mnsync", &mnsync, true }, { "dash", "spork", &spork, true }, { "dash", "getpoolinfo", &getpoolinfo, true }, + { "dash", "sentinelping", &sentinelping, true }, #ifdef ENABLE_WALLET { "dash", "privatesend", &privatesend, false }, diff --git a/src/rpc/server.h b/src/rpc/server.h index d092f6b89..5a32151db 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -296,6 +296,7 @@ extern UniValue getchaintips(const UniValue& params, bool fHelp); extern UniValue invalidateblock(const UniValue& params, bool fHelp); extern UniValue reconsiderblock(const UniValue& params, bool fHelp); extern UniValue getspentinfo(const UniValue& params, bool fHelp); +extern UniValue sentinelping(const UniValue& params, bool fHelp); bool StartRPC(); void InterruptRPC(); diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index 973c8eab1..56ce1d3c7 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -307,4 +307,11 @@ BOOST_AUTO_TEST_CASE(rpc_ban) BOOST_CHECK_EQUAL(adr.get_str(), "2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/128"); } +BOOST_AUTO_TEST_CASE(rpc_sentinel_ping) +{ + BOOST_CHECK_NO_THROW(CallRPC("sentinelping 1.0.2")); + BOOST_CHECK_THROW(CallRPC("sentinelping"), runtime_error); + BOOST_CHECK_THROW(CallRPC("sentinelping 2"), bad_cast); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp index 202793897..3b76f2129 100644 --- a/src/test/serialize_tests.cpp +++ b/src/test/serialize_tests.cpp @@ -276,4 +276,53 @@ BOOST_AUTO_TEST_CASE(insert_delete) BOOST_CHECK_EQUAL(ss.size(), 0); } +// Change struct size and check if it can be deserialized +// from old version archive and vice versa +struct old_version +{ + int field1; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(field1); + } +};\ +struct new_version +{ + int field1; + int field2; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(field1); + if(ser_action.ForRead() && (s.size() == 0)) + { + field2 = 0; + return; + } + READWRITE(field2); + } +}; + +BOOST_AUTO_TEST_CASE(check_backward_compatibility) +{ + CDataStream ss(SER_DISK, 0); + old_version old_src({5}); + ss << old_src; + new_version new_dest({6, 7}); + BOOST_REQUIRE_NO_THROW(ss >> new_dest); + BOOST_REQUIRE(old_src.field1 == new_dest.field1); + BOOST_REQUIRE(ss.size() == 0); + + new_version new_src({6, 7}); + ss << new_src; + old_version old_dest({5}); + BOOST_REQUIRE_NO_THROW(ss >> old_dest); + BOOST_REQUIRE(new_src.field1 == old_dest.field1); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 7a8b4e113..5ac9b6928 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -489,4 +489,19 @@ BOOST_AUTO_TEST_CASE(test_ParseFixedPoint) BOOST_CHECK(!ParseFixedPoint("1.", 8, &amount)); } +BOOST_AUTO_TEST_CASE(version_info_helper) +{ + BOOST_CHECK(StringVersionToInt("1.1.1") == 0x010101); + BOOST_CHECK(IntVersionToString(0x010101) == "1.1.1"); + + BOOST_CHECK_THROW(StringVersionToInt("1.1.hgdghfgf"), bad_cast); + BOOST_CHECK_THROW(StringVersionToInt("1.1"), bad_cast); + BOOST_CHECK_THROW(StringVersionToInt("1.1.1f"), bad_cast); + BOOST_CHECK_THROW(StringVersionToInt("1.1.1000"), bad_cast); + BOOST_CHECK_THROW(StringVersionToInt("10"), bad_cast); + BOOST_CHECK_THROW(StringVersionToInt("1.1.1.1"), bad_cast); + BOOST_CHECK_THROW(IntVersionToString(0x01010101), bad_cast); + BOOST_CHECK_THROW(IntVersionToString(0), bad_cast); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/util.cpp b/src/util.cpp index f62bfdd50..e9002c058 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -78,6 +78,8 @@ #include // for to_lower() #include #include // for startswith() and endswith() +#include +#include #include #include #include @@ -952,3 +954,39 @@ int GetNumCores() #endif } + +uint32_t StringVersionToInt(const std::string& strVersion) +{ + std::vector tokens; + boost::split(tokens, strVersion, boost::is_any_of(".")); + if(tokens.size() != 3) + throw std::bad_cast(); + uint32_t nVersion = 0; + for(unsigned idx = 0; idx < 3; idx++) + { + if(tokens[idx].length() == 0) + throw std::bad_cast(); + uint32_t value = boost::lexical_cast(tokens[idx]); + if(value > 255) + throw std::bad_cast(); + nVersion <<= 8; + nVersion |= value; + } + return nVersion; +} + +std::string IntVersionToString(uint32_t nVersion) +{ + if((nVersion >> 24) > 0) // MSB is always 0 + throw std::bad_cast(); + if(nVersion == 0) + throw std::bad_cast(); + std::array tokens; + for(unsigned idx = 0; idx < 3; idx++) + { + unsigned shift = (2 - idx) * 8; + uint32_t byteValue = (nVersion >> shift) & 0xff; + tokens[idx] = boost::lexical_cast(byteValue); + } + return boost::join(tokens, "."); +} diff --git a/src/util.h b/src/util.h index 296bf3ac0..129fdd721 100644 --- a/src/util.h +++ b/src/util.h @@ -271,4 +271,23 @@ template void TraceThread(const char* name, Callable func) } } + +/** + * @brief Converts version strings to 4-byte unsigned integer + * @param strVersion version in "x.x.x" format (decimal digits only) + * @return 4-byte unsigned integer, most significant byte is always 0 + * Throws std::bad_cast if format doesn\t match. + */ +uint32_t StringVersionToInt(const std::string& strVersion); + + +/** + * @brief Converts version as 4-byte unsigned integer to string + * @param nVersion 4-byte unsigned integer, most significant byte is always 0 + * @return version string in "x.x.x" format (last 3 bytes as version parts) + * Throws std::bad_cast if format doesn\t match. + */ +std::string IntVersionToString(uint32_t nVersion); + + #endif // BITCOIN_UTIL_H diff --git a/src/version.h b/src/version.h index 52412a77d..5506c18f8 100644 --- a/src/version.h +++ b/src/version.h @@ -10,7 +10,7 @@ * network protocol versioning */ -static const int PROTOCOL_VERSION = 70206; +static const int PROTOCOL_VERSION = 70207; //! initial proto version, to be increased after version/verack negotiation static const int INIT_PROTO_VERSION = 209;