From 451f7f0710f7fca8ae8300cb0254189450a1c023 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Thu, 15 Feb 2018 17:44:22 +0300 Subject: [PATCH] Fix issues with mnp, mnw and dsq signatures via new spork (SPORK_6_NEW_SIGS) (#1936) * Use new spork SPORK_6_NEW_SIGS to fix masternode payment vote signature format * Use SPORK_6_NEW_SIGS to also fix masternode ping signature format * Use SPORK_6_NEW_SIGS to also fix privatesend queue signature format * adjust spork6 description in docs * mnp hashing/signing changed - hash everything and use SignHash/VerifyHash directly instead of constructing a string to sign (both activate via SPORK_6_NEW_SIGS) * fix * cleanup --- dash-docs/protocol-documentation.md | 1 + src/masternode-payments.cpp | 28 ++++++++++- src/masternode.cpp | 77 +++++++++++++++++++++++------ src/masternode.h | 24 +++++---- src/privatesend.cpp | 26 +++++++++- src/spork.cpp | 4 ++ src/spork.h | 2 + 7 files changed, 132 insertions(+), 30 deletions(-) diff --git a/dash-docs/protocol-documentation.md b/dash-docs/protocol-documentation.md index a6d4e459b..c0c73388f 100644 --- a/dash-docs/protocol-documentation.md +++ b/dash-docs/protocol-documentation.md @@ -254,6 +254,7 @@ Spork | 10001 | 2 | INSTANTSEND_ENABLED | Turns on and off InstantSend network wide | 10002 | 3 | INSTANTSEND_BLOCK_FILTERING | Turns on and off InstantSend block filtering | 10004 | 5 | INSTANTSEND_MAX_VALUE | Controls the max value for an InstantSend transaction (currently 2000 dash) +| 10005 | 6 | NEW_SIGS | Turns on and off new signature format for some messages (mnp, mnw, dsq) | 10007 | 8 | MASTERNODE_PAYMENT_ENFORCEMENT | Requires masternodes to be paid by miners when blocks are processed | 10008 | 9 | SUPERBLOCKS_ENABLED | Superblocks are enabled (the 10% comes to fund the dash treasury) | 10009 | 10 | MASTERNODE_PAY_UPDATED_NODES | Only current protocol version masternode's will be paid (not older nodes) diff --git a/src/masternode-payments.cpp b/src/masternode-payments.cpp index 2182d2728..46a661621 100644 --- a/src/masternode-payments.cpp +++ b/src/masternode-payments.cpp @@ -401,7 +401,7 @@ bool CMasternodePaymentVote::Sign() std::string strError; std::string strMessage = masternodeOutpoint.ToStringShort() + boost::lexical_cast(nBlockHeight) + - ScriptToAsmStr(payee); + (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS) ? HexStr(payee) : ScriptToAsmStr(payee)); if(!CMessageSigner::SignMessage(strMessage, vchSig, activeMasternode.keyMasternode)) { LogPrintf("CMasternodePaymentVote::Sign -- SignMessage() failed\n"); @@ -838,10 +838,34 @@ bool CMasternodePaymentVote::CheckSignature(const CPubKey& pubKeyMasternode, int std::string strMessage = masternodeOutpoint.ToStringShort() + boost::lexical_cast(nBlockHeight) + - ScriptToAsmStr(payee); + (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS) ? HexStr(payee) : ScriptToAsmStr(payee)); std::string strError = ""; if (!CMessageSigner::VerifyMessage(pubKeyMasternode, vchSig, strMessage, strError)) { + if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) { + // Try old format even though we activated signing via new one already. + // Could be an old vote signed earlier or a vote signed by a non-upgraded MN. + std::string strErrorOld = ""; + strMessage = masternodeOutpoint.ToStringShort() + + boost::lexical_cast(nBlockHeight) + + ScriptToAsmStr(payee); + + if (!CMessageSigner::VerifyMessage(pubKeyMasternode, vchSig, strMessage, strErrorOld)) { + // Neither format worked, definitely an invalid signature. + // Only ban for future block vote when we are already synced. + // Otherwise it could be the case when MN which signed this vote is using another key now + // and we have no idea about the old one. + if(masternodeSync.IsMasternodeListSynced() && nBlockHeight > nValidationHeight) { + nDos = 20; + } + return error("CMasternodePaymentVote::CheckSignature -- Got bad Masternode payment signature, masternode=%s, error: %s", + masternodeOutpoint.ToStringShort(), strErrorOld); + } + // Was indeed a valid signature in old format + LogPrintf("Masternode %s send payment vote in old format, new format \n", masternodeOutpoint.ToStringShort()); + return true; + } + // Spork is off but message signature is invalid. // Only ban for future block vote when we are already synced. // Otherwise it could be the case when MN which signed this vote is using another key now // and we have no idea about the old one. diff --git a/src/masternode.cpp b/src/masternode.cpp index 2a46ca276..bb6a15b0b 100644 --- a/src/masternode.cpp +++ b/src/masternode.cpp @@ -4,6 +4,7 @@ #include "activemasternode.h" #include "base58.h" +#include "clientversion.h" #include "init.h" #include "netbase.h" #include "masternode.h" @@ -635,6 +636,23 @@ void CMasternodeBroadcast::Relay(CConnman& connman) const connman.RelayInv(inv); } +uint256 CMasternodePing::GetHash() const +{ + CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); + if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) { + ss << masternodeOutpoint; + ss << blockHash; + ss << sigTime; + ss << fSentinelIsCurrent; + ss << nSentinelVersion; + ss << nDaemonVersion; + } else { + ss << masternodeOutpoint << uint8_t{} << 0xffffffff; // adding dummy values here to match old hashing format + ss << sigTime; + } + return ss.GetHash(); +} + CMasternodePing::CMasternodePing(const COutPoint& outpoint) { LOCK(cs_main); @@ -643,25 +661,40 @@ CMasternodePing::CMasternodePing(const COutPoint& outpoint) masternodeOutpoint = outpoint; blockHash = chainActive[chainActive.Height() - 12]->GetBlockHash(); sigTime = GetAdjustedTime(); + nDaemonVersion = CLIENT_VERSION; } bool CMasternodePing::Sign(const CKey& keyMasternode, const CPubKey& pubKeyMasternode) { std::string strError; - std::string strMasterNodeSignMessage; - // TODO: add sentinel data sigTime = GetAdjustedTime(); - std::string strMessage = CTxIn(masternodeOutpoint).ToString() + blockHash.ToString() + boost::lexical_cast(sigTime); - if(!CMessageSigner::SignMessage(strMessage, vchSig, keyMasternode)) { - LogPrintf("CMasternodePing::Sign -- SignMessage() failed\n"); - return false; - } + if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) { + uint256 hash = GetHash(); - if(!CMessageSigner::VerifyMessage(pubKeyMasternode, vchSig, strMessage, strError)) { - LogPrintf("CMasternodePing::Sign -- VerifyMessage() failed, error: %s\n", strError); - return false; + if (!CHashSigner::SignHash(hash, keyMasternode, vchSig)) { + LogPrintf("CMasternodePing::Sign -- SignHash() failed\n"); + return false; + } + + if (!CHashSigner::VerifyHash(hash, pubKeyMasternode, vchSig, strError)) { + LogPrintf("CMasternodePing::Sign -- VerifyHash() failed, error: %s\n", strError); + return false; + } + } else { + std::string strMessage = CTxIn(masternodeOutpoint).ToString() + blockHash.ToString() + + boost::lexical_cast(sigTime); + + if (!CMessageSigner::SignMessage(strMessage, vchSig, keyMasternode)) { + LogPrintf("CMasternodePing::Sign -- SignMessage() failed\n"); + return false; + } + + if (!CMessageSigner::VerifyMessage(pubKeyMasternode, vchSig, strMessage, strError)) { + LogPrintf("CMasternodePing::Sign -- VerifyMessage() failed, error: %s\n", strError); + return false; + } } return true; @@ -669,16 +702,28 @@ bool CMasternodePing::Sign(const CKey& keyMasternode, const CPubKey& pubKeyMaste bool CMasternodePing::CheckSignature(const CPubKey& pubKeyMasternode, int &nDos) { - // TODO: add sentinel data - std::string strMessage = CTxIn(masternodeOutpoint).ToString() + blockHash.ToString() + boost::lexical_cast(sigTime); std::string strError = ""; nDos = 0; - if(!CMessageSigner::VerifyMessage(pubKeyMasternode, vchSig, strMessage, strError)) { - LogPrintf("CMasternodePing::CheckSignature -- Got bad Masternode ping signature, masternode=%s, error: %s\n", masternodeOutpoint.ToStringShort(), strError); - nDos = 33; - return false; + if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) { + uint256 hash = GetHash(); + + if (!CHashSigner::VerifyHash(hash, pubKeyMasternode, vchSig, strError)) { + LogPrintf("CMasternodePing::CheckSignature -- Got bad Masternode ping signature, masternode=%s, error: %s\n", masternodeOutpoint.ToStringShort(), strError); + nDos = 33; + return false; + } + } else { + std::string strMessage = CTxIn(masternodeOutpoint).ToString() + blockHash.ToString() + + boost::lexical_cast(sigTime); + + if (!CMessageSigner::VerifyMessage(pubKeyMasternode, vchSig, strMessage, strError)) { + LogPrintf("CMasternodePing::CheckSignature -- Got bad Masternode ping signature, masternode=%s, error: %s\n", masternodeOutpoint.ToStringShort(), strError); + nDos = 33; + return false; + } } + return true; } diff --git a/src/masternode.h b/src/masternode.h index 94f145531..478924ef4 100644 --- a/src/masternode.h +++ b/src/masternode.h @@ -26,8 +26,10 @@ 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 // -// sentinel version before sentinel ping implementation +// sentinel version before implementation of nSentinelVersion in CMasternodePing #define DEFAULT_SENTINEL_VERSION 0x010001 +// daemon version before implementation of nDaemonVersion in CMasternodePing +#define DEFAULT_DAEMON_VERSION 120200 class CMasternodePing { @@ -39,6 +41,7 @@ public: bool fSentinelIsCurrent = false; // true if last sentinel ping was actual // MSB is always 0, other 3 bits corresponds to x.x.x version scheme uint32_t nSentinelVersion{DEFAULT_SENTINEL_VERSION}; + uint32_t nDaemonVersion{DEFAULT_DAEMON_VERSION}; CMasternodePing() = default; @@ -66,23 +69,24 @@ public: READWRITE(blockHash); READWRITE(sigTime); READWRITE(vchSig); - if(ser_action.ForRead() && (s.size() == 0)) - { + if(ser_action.ForRead() && s.size() == 0) { fSentinelIsCurrent = false; nSentinelVersion = DEFAULT_SENTINEL_VERSION; + nDaemonVersion = DEFAULT_DAEMON_VERSION; return; } READWRITE(fSentinelIsCurrent); READWRITE(nSentinelVersion); + if(ser_action.ForRead() && s.size() == 0) { + nDaemonVersion = DEFAULT_DAEMON_VERSION; + return; + } + if (nVersion > 70208) { + READWRITE(nDaemonVersion); + } } - uint256 GetHash() const - { - CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); - ss << masternodeOutpoint << uint8_t{} << 0xffffffff; - ss << sigTime; - return ss.GetHash(); - } + uint256 GetHash() const; bool IsExpired() const { return GetAdjustedTime() - sigTime > MASTERNODE_NEW_START_REQUIRED_SECONDS; } diff --git a/src/privatesend.cpp b/src/privatesend.cpp index 14c36a948..52c03712b 100644 --- a/src/privatesend.cpp +++ b/src/privatesend.cpp @@ -40,7 +40,18 @@ bool CDarksendQueue::Sign() { if(!fMasternodeMode) return false; - std::string strMessage = CTxIn(masternodeOutpoint).ToString() + boost::lexical_cast(nDenom) + boost::lexical_cast(nTime) + boost::lexical_cast(fReady); + std::string strMessage; + if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) { + strMessage = masternodeOutpoint.ToStringShort() + + boost::lexical_cast(nDenom) + + boost::lexical_cast(nTime) + + boost::lexical_cast(fReady); + } else { + strMessage = CTxIn(masternodeOutpoint).ToString() + + boost::lexical_cast(nDenom) + + boost::lexical_cast(nTime) + + boost::lexical_cast(fReady); + } if(!CMessageSigner::SignMessage(strMessage, vchSig, activeMasternode.keyMasternode)) { LogPrintf("CDarksendQueue::Sign -- SignMessage() failed, %s\n", ToString()); @@ -52,8 +63,19 @@ bool CDarksendQueue::Sign() bool CDarksendQueue::CheckSignature(const CPubKey& pubKeyMasternode) { - std::string strMessage = CTxIn(masternodeOutpoint).ToString() + boost::lexical_cast(nDenom) + boost::lexical_cast(nTime) + boost::lexical_cast(fReady); std::string strError = ""; + std::string strMessage; + if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) { + strMessage = masternodeOutpoint.ToStringShort() + + boost::lexical_cast(nDenom) + + boost::lexical_cast(nTime) + + boost::lexical_cast(fReady); + } else { + strMessage = CTxIn(masternodeOutpoint).ToString() + + boost::lexical_cast(nDenom) + + boost::lexical_cast(nTime) + + boost::lexical_cast(fReady); + } if(!CMessageSigner::VerifyMessage(pubKeyMasternode, vchSig, strMessage, strError)) { LogPrintf("CDarksendQueue::CheckSignature -- Got bad Masternode queue signature: %s; error: %s\n", ToString(), strError); diff --git a/src/spork.cpp b/src/spork.cpp index bc09771d8..074a49a87 100644 --- a/src/spork.cpp +++ b/src/spork.cpp @@ -129,6 +129,7 @@ bool CSporkManager::IsSporkActive(int nSporkID) case SPORK_2_INSTANTSEND_ENABLED: r = SPORK_2_INSTANTSEND_ENABLED_DEFAULT; break; case SPORK_3_INSTANTSEND_BLOCK_FILTERING: r = SPORK_3_INSTANTSEND_BLOCK_FILTERING_DEFAULT; break; case SPORK_5_INSTANTSEND_MAX_VALUE: r = SPORK_5_INSTANTSEND_MAX_VALUE_DEFAULT; break; + case SPORK_6_NEW_SIGS: r = SPORK_6_NEW_SIGS_DEFAULT; break; case SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT: r = SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT_DEFAULT; break; case SPORK_9_SUPERBLOCKS_ENABLED: r = SPORK_9_SUPERBLOCKS_ENABLED_DEFAULT; break; case SPORK_10_MASTERNODE_PAY_UPDATED_NODES: r = SPORK_10_MASTERNODE_PAY_UPDATED_NODES_DEFAULT; break; @@ -154,6 +155,7 @@ int64_t CSporkManager::GetSporkValue(int nSporkID) case SPORK_2_INSTANTSEND_ENABLED: return SPORK_2_INSTANTSEND_ENABLED_DEFAULT; case SPORK_3_INSTANTSEND_BLOCK_FILTERING: return SPORK_3_INSTANTSEND_BLOCK_FILTERING_DEFAULT; case SPORK_5_INSTANTSEND_MAX_VALUE: return SPORK_5_INSTANTSEND_MAX_VALUE_DEFAULT; + case SPORK_6_NEW_SIGS: return SPORK_6_NEW_SIGS_DEFAULT; case SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT: return SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT_DEFAULT; case SPORK_9_SUPERBLOCKS_ENABLED: return SPORK_9_SUPERBLOCKS_ENABLED_DEFAULT; case SPORK_10_MASTERNODE_PAY_UPDATED_NODES: return SPORK_10_MASTERNODE_PAY_UPDATED_NODES_DEFAULT; @@ -171,6 +173,7 @@ int CSporkManager::GetSporkIDByName(const std::string& strName) if (strName == "SPORK_2_INSTANTSEND_ENABLED") return SPORK_2_INSTANTSEND_ENABLED; if (strName == "SPORK_3_INSTANTSEND_BLOCK_FILTERING") return SPORK_3_INSTANTSEND_BLOCK_FILTERING; if (strName == "SPORK_5_INSTANTSEND_MAX_VALUE") return SPORK_5_INSTANTSEND_MAX_VALUE; + if (strName == "SPORK_6_NEW_SIGS") return SPORK_6_NEW_SIGS; if (strName == "SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT") return SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT; if (strName == "SPORK_9_SUPERBLOCKS_ENABLED") return SPORK_9_SUPERBLOCKS_ENABLED; if (strName == "SPORK_10_MASTERNODE_PAY_UPDATED_NODES") return SPORK_10_MASTERNODE_PAY_UPDATED_NODES; @@ -187,6 +190,7 @@ std::string CSporkManager::GetSporkNameByID(int nSporkID) case SPORK_2_INSTANTSEND_ENABLED: return "SPORK_2_INSTANTSEND_ENABLED"; case SPORK_3_INSTANTSEND_BLOCK_FILTERING: return "SPORK_3_INSTANTSEND_BLOCK_FILTERING"; case SPORK_5_INSTANTSEND_MAX_VALUE: return "SPORK_5_INSTANTSEND_MAX_VALUE"; + case SPORK_6_NEW_SIGS: return "SPORK_6_NEW_SIGS"; case SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT: return "SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT"; case SPORK_9_SUPERBLOCKS_ENABLED: return "SPORK_9_SUPERBLOCKS_ENABLED"; case SPORK_10_MASTERNODE_PAY_UPDATED_NODES: return "SPORK_10_MASTERNODE_PAY_UPDATED_NODES"; diff --git a/src/spork.h b/src/spork.h index 6f50f6a3c..6897b1024 100644 --- a/src/spork.h +++ b/src/spork.h @@ -22,6 +22,7 @@ static const int SPORK_END = 10013; static const int SPORK_2_INSTANTSEND_ENABLED = 10001; static const int SPORK_3_INSTANTSEND_BLOCK_FILTERING = 10002; static const int SPORK_5_INSTANTSEND_MAX_VALUE = 10004; +static const int SPORK_6_NEW_SIGS = 10005; static const int SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT = 10007; static const int SPORK_9_SUPERBLOCKS_ENABLED = 10008; static const int SPORK_10_MASTERNODE_PAY_UPDATED_NODES = 10009; @@ -31,6 +32,7 @@ static const int SPORK_14_REQUIRE_SENTINEL_FLAG = 10013; static const int64_t SPORK_2_INSTANTSEND_ENABLED_DEFAULT = 0; // ON static const int64_t SPORK_3_INSTANTSEND_BLOCK_FILTERING_DEFAULT = 0; // ON static const int64_t SPORK_5_INSTANTSEND_MAX_VALUE_DEFAULT = 1000; // 1000 DASH +static const int64_t SPORK_6_NEW_SIGS_DEFAULT = 4070908800ULL;// OFF static const int64_t SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT_DEFAULT = 4070908800ULL;// OFF static const int64_t SPORK_9_SUPERBLOCKS_ENABLED_DEFAULT = 4070908800ULL;// OFF static const int64_t SPORK_10_MASTERNODE_PAY_UPDATED_NODES_DEFAULT = 4070908800ULL;// OFF