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:
gladcow 2017-07-04 20:31:57 +03:00 committed by UdjinM6
parent f65017cfeb
commit a439e98408
15 changed files with 202 additions and 7 deletions

View File

@ -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);

View File

@ -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();

View File

@ -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;
}
/**

View File

@ -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)
{

View File

@ -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);

View File

@ -972,3 +972,4 @@ UniValue getsuperblockbudget(const UniValue& params, bool fHelp)
return strBudget;
}

View File

@ -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;
}

View File

@ -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 },

View File

@ -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();

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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, ".");
}

View File

@ -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

View File

@ -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;