// Copyright (c) 2014-2019 The Dash Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include #include const std::string CSporkManager::SERIALIZATION_VERSION_STRING = "CSporkManager-Version-2"; #define MAKE_SPORK_DEF(name, defaultValue) CSporkDef{name, defaultValue, #name} std::vector sporkDefs = { MAKE_SPORK_DEF(SPORK_2_INSTANTSEND_ENABLED, 4070908800ULL), // OFF MAKE_SPORK_DEF(SPORK_3_INSTANTSEND_BLOCK_FILTERING, 4070908800ULL), // OFF MAKE_SPORK_DEF(SPORK_9_SUPERBLOCKS_ENABLED, 4070908800ULL), // OFF MAKE_SPORK_DEF(SPORK_17_QUORUM_DKG_ENABLED, 4070908800ULL), // OFF MAKE_SPORK_DEF(SPORK_19_CHAINLOCKS_ENABLED, 4070908800ULL), // OFF MAKE_SPORK_DEF(SPORK_21_QUORUM_ALL_CONNECTED, 4070908800ULL), // OFF MAKE_SPORK_DEF(SPORK_23_QUORUM_POSE, 4070908800ULL), // OFF }; CSporkManager sporkManager; CSporkManager::CSporkManager() { for (auto& sporkDef : sporkDefs) { sporkDefsById.emplace(sporkDef.sporkId, &sporkDef); sporkDefsByName.emplace(sporkDef.name, &sporkDef); } } bool CSporkManager::SporkValueIsActive(SporkId nSporkID, int64_t &nActiveValueRet) const { LOCK(cs); if (!mapSporksActive.count(nSporkID)) return false; // calc how many values we have and how many signers vote for every value std::unordered_map mapValueCounts; for (const auto& pair: mapSporksActive.at(nSporkID)) { mapValueCounts[pair.second.nValue]++; if (mapValueCounts.at(pair.second.nValue) >= nMinSporkKeys) { // nMinSporkKeys is always more than the half of the max spork keys number, // so there is only one such value and we can stop here nActiveValueRet = pair.second.nValue; return true; } } return false; } void CSporkManager::Clear() { LOCK(cs); mapSporksActive.clear(); mapSporksByHash.clear(); // sporkPubKeyID and sporkPrivKey should be set in init.cpp, // we should not alter them here. } void CSporkManager::CheckAndRemove() { LOCK(cs); bool fSporkAddressIsSet = !setSporkPubKeyIDs.empty(); assert(fSporkAddressIsSet); auto itActive = mapSporksActive.begin(); while (itActive != mapSporksActive.end()) { auto itSignerPair = itActive->second.begin(); while (itSignerPair != itActive->second.end()) { bool fHasValidSig = setSporkPubKeyIDs.find(itSignerPair->first) != setSporkPubKeyIDs.end() && itSignerPair->second.CheckSignature(itSignerPair->first); if (!fHasValidSig) { mapSporksByHash.erase(itSignerPair->second.GetHash()); itActive->second.erase(itSignerPair++); continue; } ++itSignerPair; } if (itActive->second.empty()) { mapSporksActive.erase(itActive++); continue; } ++itActive; } auto itByHash = mapSporksByHash.begin(); while (itByHash != mapSporksByHash.end()) { bool found = false; for (const auto& signer: setSporkPubKeyIDs) { if (itByHash->second.CheckSignature(signer)) { found = true; break; } } if (!found) { mapSporksByHash.erase(itByHash++); continue; } ++itByHash; } } void CSporkManager::ProcessSpork(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman) { if (strCommand == NetMsgType::SPORK) { CSporkMessage spork; vRecv >> spork; uint256 hash = spork.GetHash(); std::string strLogMsg; { LOCK(cs_main); EraseObjectRequest(pfrom->GetId(), CInv(MSG_SPORK, hash)); if(!chainActive.Tip()) return; strLogMsg = strprintf("SPORK -- hash: %s id: %d value: %10d bestHeight: %d peer=%d", hash.ToString(), spork.nSporkID, spork.nValue, chainActive.Height(), pfrom->GetId()); } if (spork.nTimeSigned > GetAdjustedTime() + 2 * 60 * 60) { LOCK(cs_main); LogPrint(BCLog::SPORK, "CSporkManager::ProcessSpork -- ERROR: too far into the future\n"); Misbehaving(pfrom->GetId(), 100); return; } CKeyID keyIDSigner; if (!spork.GetSignerKeyID(keyIDSigner) || !setSporkPubKeyIDs.count(keyIDSigner)) { LOCK(cs_main); LogPrint(BCLog::SPORK, "CSporkManager::ProcessSpork -- ERROR: invalid signature\n"); Misbehaving(pfrom->GetId(), 100); return; } { LOCK(cs); // make sure to not lock this together with cs_main if (mapSporksActive.count(spork.nSporkID)) { if (mapSporksActive[spork.nSporkID].count(keyIDSigner)) { if (mapSporksActive[spork.nSporkID][keyIDSigner].nTimeSigned >= spork.nTimeSigned) { LogPrint(BCLog::SPORK, "%s seen\n", strLogMsg); return; } else { LogPrintf("%s updated\n", strLogMsg); } } else { LogPrintf("%s new signer\n", strLogMsg); } } else { LogPrintf("%s new\n", strLogMsg); } } { LOCK(cs); // make sure to not lock this together with cs_main mapSporksByHash[hash] = spork; mapSporksActive[spork.nSporkID][keyIDSigner] = spork; } spork.Relay(connman); } else if (strCommand == NetMsgType::GETSPORKS) { LOCK(cs); // make sure to not lock this together with cs_main for (const auto& pair : mapSporksActive) { for (const auto& signerSporkPair: pair.second) { connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::SPORK, signerSporkPair.second)); } } } } bool CSporkManager::UpdateSpork(SporkId nSporkID, int64_t nValue, CConnman& connman) { CSporkMessage spork = CSporkMessage(nSporkID, nValue, GetAdjustedTime()); if (!spork.Sign(sporkPrivKey)) { LogPrintf("CSporkManager::%s -- ERROR: signing failed for spork %d\n", __func__, nSporkID); return false; } CKeyID keyIDSigner; if (!spork.GetSignerKeyID(keyIDSigner) || !setSporkPubKeyIDs.count(keyIDSigner)) { LogPrintf("CSporkManager::UpdateSpork: failed to find keyid for private key\n"); return false; } LogPrintf("CSporkManager::%s -- signed %d %s\n", __func__, nSporkID, spork.GetHash().ToString()); { LOCK(cs); mapSporksByHash[spork.GetHash()] = spork; mapSporksActive[nSporkID][keyIDSigner] = spork; } spork.Relay(connman); return true; } bool CSporkManager::IsSporkActive(SporkId nSporkID) { int64_t nSporkValue = GetSporkValue(nSporkID); return nSporkValue < GetAdjustedTime(); } int64_t CSporkManager::GetSporkValue(SporkId nSporkID) { LOCK(cs); int64_t nSporkValue = -1; if (SporkValueIsActive(nSporkID, nSporkValue)) { return nSporkValue; } auto it = sporkDefsById.find(nSporkID); if (it != sporkDefsById.end()) { return it->second->defaultValue; } LogPrint(BCLog::SPORK, "CSporkManager::GetSporkValue -- Unknown Spork ID %d\n", nSporkID); return -1; } SporkId CSporkManager::GetSporkIDByName(const std::string& strName) { auto it = sporkDefsByName.find(strName); if (it == sporkDefsByName.end()) { LogPrint(BCLog::SPORK, "CSporkManager::GetSporkIDByName -- Unknown Spork name '%s'\n", strName); return SPORK_INVALID; } return it->second->sporkId; } std::string CSporkManager::GetSporkNameByID(SporkId nSporkID) { auto it = sporkDefsById.find(nSporkID); if (it == sporkDefsById.end()) { LogPrint(BCLog::SPORK, "CSporkManager::GetSporkNameByID -- Unknown Spork ID %d\n", nSporkID); return "Unknown"; } return it->second->name; } bool CSporkManager::GetSporkByHash(const uint256& hash, CSporkMessage &sporkRet) { LOCK(cs); const auto it = mapSporksByHash.find(hash); if (it == mapSporksByHash.end()) return false; sporkRet = it->second; return true; } bool CSporkManager::SetSporkAddress(const std::string& strAddress) { LOCK(cs); CTxDestination dest = DecodeDestination(strAddress); const CKeyID *keyID = boost::get(&dest); if (!keyID) { LogPrintf("CSporkManager::SetSporkAddress -- Failed to parse spork address\n"); return false; } setSporkPubKeyIDs.insert(*keyID); return true; } bool CSporkManager::SetMinSporkKeys(int minSporkKeys) { int maxKeysNumber = setSporkPubKeyIDs.size(); if ((minSporkKeys <= maxKeysNumber / 2) || (minSporkKeys > maxKeysNumber)) { LogPrintf("CSporkManager::SetMinSporkKeys -- Invalid min spork signers number: %d\n", minSporkKeys); return false; } nMinSporkKeys = minSporkKeys; return true; } bool CSporkManager::SetPrivKey(const std::string& strPrivKey) { CKey key; CPubKey pubKey; if(!CMessageSigner::GetKeysFromSecret(strPrivKey, key, pubKey)) { LogPrintf("CSporkManager::SetPrivKey -- Failed to parse private key\n"); return false; } if (setSporkPubKeyIDs.find(pubKey.GetID()) == setSporkPubKeyIDs.end()) { LogPrintf("CSporkManager::SetPrivKey -- New private key does not belong to spork addresses\n"); return false; } CSporkMessage spork; if (!spork.Sign(key)) { LogPrintf("CSporkManager::SetPrivKey -- Test signing failed\n"); return false; } // Test signing successful, proceed LOCK(cs); LogPrintf("CSporkManager::SetPrivKey -- Successfully initialized as spork signer\n"); sporkPrivKey = key; return true; } std::string CSporkManager::ToString() const { LOCK(cs); return strprintf("Sporks: %llu", mapSporksActive.size()); } uint256 CSporkMessage::GetHash() const { return SerializeHash(*this); } uint256 CSporkMessage::GetSignatureHash() const { CHashWriter s(SER_GETHASH, 0); s << nSporkID; s << nValue; s << nTimeSigned; return s.GetHash(); } bool CSporkMessage::Sign(const CKey& key) { if (!key.IsValid()) { LogPrintf("CSporkMessage::Sign -- signing key is not valid\n"); return false; } CKeyID pubKeyId = key.GetPubKey().GetID(); std::string strError = ""; // Harden Spork6 so that it is active on testnet and no other networks if (Params().NetworkIDString() == CBaseChainParams::TESTNET) { uint256 hash = GetSignatureHash(); if(!CHashSigner::SignHash(hash, key, vchSig)) { LogPrintf("CSporkMessage::Sign -- SignHash() failed\n"); return false; } if (!CHashSigner::VerifyHash(hash, pubKeyId, vchSig, strError)) { LogPrintf("CSporkMessage::Sign -- VerifyHash() failed, error: %s\n", strError); return false; } } else { std::string strMessage = std::to_string(nSporkID) + std::to_string(nValue) + std::to_string(nTimeSigned); if(!CMessageSigner::SignMessage(strMessage, vchSig, key)) { LogPrintf("CSporkMessage::Sign -- SignMessage() failed\n"); return false; } if(!CMessageSigner::VerifyMessage(pubKeyId, vchSig, strMessage, strError)) { LogPrintf("CSporkMessage::Sign -- VerifyMessage() failed, error: %s\n", strError); return false; } } return true; } bool CSporkMessage::CheckSignature(const CKeyID& pubKeyId) const { std::string strError = ""; // Harden Spork6 so that it is active on testnet and no other networks if (Params().NetworkIDString() == CBaseChainParams::TESTNET) { uint256 hash = GetSignatureHash(); if (!CHashSigner::VerifyHash(hash, pubKeyId, vchSig, strError)) { LogPrint(BCLog::SPORK, "CSporkMessage::CheckSignature -- VerifyHash() failed, error: %s\n", strError); return false; } } else { std::string strMessage = std::to_string(nSporkID) + std::to_string(nValue) + std::to_string(nTimeSigned); if (!CMessageSigner::VerifyMessage(pubKeyId, vchSig, strMessage, strError)){ LogPrint(BCLog::SPORK, "CSporkMessage::CheckSignature -- VerifyMessage() failed, error: %s\n", strError); return false; } } return true; } bool CSporkMessage::GetSignerKeyID(CKeyID &retKeyidSporkSigner) { CPubKey pubkeyFromSig; // Harden Spork6 so that it is active on testnet and no other networks if (Params().NetworkIDString() == CBaseChainParams::TESTNET) { if (!pubkeyFromSig.RecoverCompact(GetSignatureHash(), vchSig)) { return false; } } else { std::string strMessage = std::to_string(nSporkID) + std::to_string(nValue) + std::to_string(nTimeSigned); CHashWriter ss(SER_GETHASH, 0); ss << strMessageMagic; ss << strMessage; if (!pubkeyFromSig.RecoverCompact(ss.GetHash(), vchSig)) { return false; } } retKeyidSporkSigner = pubkeyFromSig.GetID(); return true; } void CSporkMessage::Relay(CConnman& connman) { CInv inv(MSG_SPORK, GetHash()); connman.RelayInv(inv); }