Clear votes which were created before spork15 activation and use operator key for non-funding votes (#2512)

* Clear votes which were created before spork15 activation

* Reject incoming votes which were created pre-DIP3

* Only use voting keys for VOTE_SIGNAL_FUNDING

The other vote signals are meant to be emitted by sentinel and must thus
be signed with the operator key.

* Simplify GetMinVoteTime

* Review suggestions/fixes

* Add missing mutex in CGovernanceObject::RemoveOldVotes
This commit is contained in:
Alexander Block 2018-11-29 19:51:53 +01:00 committed by UdjinM6
parent f6f6d075dc
commit 0c1b683a06
7 changed files with 96 additions and 3 deletions

View File

@ -177,7 +177,7 @@ bool CGovernanceObject::ProcessVote(CNode* pfrom,
}
}
bool onlyOwnerAllowed = nObjectType == GOVERNANCE_OBJECT_PROPOSAL;
bool onlyOwnerAllowed = nObjectType == GOVERNANCE_OBJECT_PROPOSAL && vote.GetSignal() == VOTE_SIGNAL_FUNDING;
// Finally check that the vote is actually valid (done last because of cost of signature verification)
if (!vote.IsValid(onlyOwnerAllowed)) {
@ -790,3 +790,21 @@ void CGovernanceObject::CheckOrphanVotes(CConnman& connman)
}
}
}
std::vector<uint256> CGovernanceObject::RemoveOldVotes(unsigned int nMinTime)
{
LOCK(cs);
auto removed = fileVotes.RemoveOldVotes(nMinTime);
if (!removed.empty()) {
std::string removedStr;
for (auto& h : removed) {
removedStr += strprintf(" %s\n", h.ToString());
}
LogPrintf("CGovernanceObject::RemoveOldVotes -- Removed %d old (pre-DIP3) votes for %s:\n%s\n", removed.size(), GetHash().ToString(), removedStr);
fDirtyCache = true;
}
return removed;
}

View File

@ -350,6 +350,9 @@ private:
void ClearMasternodeVotes();
void CheckOrphanVotes(CConnman& connman);
// TODO can be removed after DIP3 is fully deployed
std::vector<uint256> RemoveOldVotes(unsigned int nMinTime);
};

View File

@ -68,6 +68,23 @@ void CGovernanceObjectVoteFile::RemoveVotesFromMasternode(const COutPoint& outpo
}
}
std::vector<uint256> CGovernanceObjectVoteFile::RemoveOldVotes(unsigned int nMinTime)
{
std::vector<uint256> removed;
vote_l_it it = listVotes.begin();
while (it != listVotes.end()) {
if (it->GetTimestamp() < nMinTime) {
--nMemoryVotes;
removed.emplace_back(it->GetHash());
mapVoteIndex.erase(it->GetHash());
listVotes.erase(it++);
} else {
++it;
}
}
return removed;
}
void CGovernanceObjectVoteFile::RebuildIndex()
{
mapVoteIndex.clear();

View File

@ -74,6 +74,9 @@ public:
void RemoveVotesFromMasternode(const COutPoint& outpointMasternode);
// TODO can be removed after full DIP3 deployment
std::vector<uint256> RemoveOldVotes(unsigned int nMinTime);
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>

View File

@ -222,6 +222,12 @@ void CGovernanceManager::ProcessMessage(CNode* pfrom, const std::string& strComm
CGovernanceVote vote;
vRecv >> vote;
// TODO remove this check after full DIP3 deployment
if (vote.GetTimestamp() < GetMinVoteTime()) {
// Ignore votes pre-DIP3
return;
}
uint256 nHash = vote.GetHash();
{
@ -573,6 +579,10 @@ void CGovernanceManager::DoMaintenance(CConnman& connman)
{
if (fLiteMode || !masternodeSync.IsSynced() || ShutdownRequested()) return;
if (deterministicMNManager->IsDeterministicMNsSporkActive()) {
ClearPreDIP3Votes();
}
// CHECK OBJECTS WE'VE ASKED FOR, REMOVE OLD ENTRIES
CleanOrphanObjects();
@ -676,7 +686,7 @@ void CGovernanceManager::SyncSingleObjAndItsVotes(CNode* pnode, const uint256& n
for (const auto& vote : fileVotes.GetVotes()) {
uint256 nVoteHash = vote.GetHash();
bool onlyOwnerAllowed = govobj.GetObjectType() == GOVERNANCE_OBJECT_PROPOSAL;
bool onlyOwnerAllowed = govobj.GetObjectType() == GOVERNANCE_OBJECT_PROPOSAL && vote.GetSignal() == VOTE_SIGNAL_FUNDING;
if (filter.contains(nVoteHash) || !vote.IsValid(onlyOwnerAllowed)) {
continue;
@ -1291,6 +1301,10 @@ void CGovernanceManager::UpdatedBlockTip(const CBlockIndex* pindex, CConnman& co
nCachedBlockHeight = pindex->nHeight;
LogPrint("gobject", "CGovernanceManager::UpdatedBlockTip -- nCachedBlockHeight: %d\n", nCachedBlockHeight);
if (deterministicMNManager->IsDeterministicMNsSporkActive(pindex->nHeight)) {
ClearPreDIP3Votes();
}
CheckPostponedObjects(connman);
CSuperblockManager::ExecuteBestSuperblock(pindex->nHeight);
@ -1342,3 +1356,37 @@ void CGovernanceManager::CleanOrphanObjects()
}
}
}
unsigned int CGovernanceManager::GetMinVoteTime()
{
LOCK(cs_main);
if (!deterministicMNManager->IsDeterministicMNsSporkActive()) {
return 0;
}
int64_t dip3SporkHeight = sporkManager.GetSporkValue(SPORK_15_DETERMINISTIC_MNS_ENABLED);
return chainActive[dip3SporkHeight]->nTime;
}
void CGovernanceManager::ClearPreDIP3Votes()
{
// This removes all votes which were created before DIP3 spork15 activation
// All these votes are invalid immediately after spork15 activation due to the introduction of voting keys, which
// are not equal to the old masternode private keys
unsigned int minVoteTime = GetMinVoteTime();
LOCK(cs);
for (auto& p : mapObjects) {
auto& obj = p.second;
auto removed = obj.RemoveOldVotes(minVoteTime);
if (removed.empty()) {
continue;
}
for (auto& voteHash : removed) {
cmapVoteToObject.Erase(voteHash);
cmapInvalidVotes.Erase(voteHash);
cmmapOrphanVotes.Erase(voteHash);
setRequestedVotes.erase(voteHash);
}
}
}

View File

@ -448,6 +448,10 @@ private:
void RequestOrphanObjects(CConnman& connman);
void CleanOrphanObjects();
// TODO can be removed after full DIP3 deployment
unsigned int GetMinVoteTime();
void ClearPreDIP3Votes();
};
#endif

View File

@ -1086,7 +1086,7 @@ UniValue voteraw(const JSONRPCRequest& request)
vote.SetTime(nTime);
vote.SetSignature(vchSig);
bool onlyOwnerAllowed = govObjType == GOVERNANCE_OBJECT_PROPOSAL;
bool onlyOwnerAllowed = govObjType == GOVERNANCE_OBJECT_PROPOSAL && vote.GetSignal() == VOTE_SIGNAL_FUNDING;
if (!vote.IsValid(onlyOwnerAllowed)) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Failure to verify vote.");