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
This commit is contained in:
parent
f65017cfeb
commit
a439e98408
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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<std::string>(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<std::string>(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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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<unsigned char> 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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -972,3 +972,4 @@ UniValue getsuperblockbudget(const UniValue& params, bool fHelp)
|
||||
|
||||
return strBudget;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 },
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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()
|
||||
|
@ -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 <typename Stream, typename Operation>
|
||||
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 <typename Stream, typename Operation>
|
||||
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()
|
||||
|
@ -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()
|
||||
|
38
src/util.cpp
38
src/util.cpp
@ -78,6 +78,8 @@
|
||||
#include <boost/algorithm/string/case_conv.hpp> // for to_lower()
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp> // for startswith() and endswith()
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <boost/algorithm/string/classification.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
@ -952,3 +954,39 @@ int GetNumCores()
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
uint32_t StringVersionToInt(const std::string& strVersion)
|
||||
{
|
||||
std::vector<std::string> 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<uint32_t>(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<std::string, 3> tokens;
|
||||
for(unsigned idx = 0; idx < 3; idx++)
|
||||
{
|
||||
unsigned shift = (2 - idx) * 8;
|
||||
uint32_t byteValue = (nVersion >> shift) & 0xff;
|
||||
tokens[idx] = boost::lexical_cast<std::string>(byteValue);
|
||||
}
|
||||
return boost::join(tokens, ".");
|
||||
}
|
||||
|
19
src/util.h
19
src/util.h
@ -271,4 +271,23 @@ template <typename Callable> 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
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user