New vote tallying implementation (#1135)

This commit is contained in:
Tim Flynn 2016-11-13 12:52:34 -05:00 committed by UdjinM6
parent 82ca5fdbb8
commit c31ba8ba4c
25 changed files with 2401 additions and 533 deletions

View File

@ -74,6 +74,8 @@ BITCOIN_CORE_H = \
arith_uint256.h \
base58.h \
bloom.h \
cachemap.h \
cachemultimap.h \
chain.h \
chainparams.h \
chainparamsbase.h \
@ -99,7 +101,9 @@ BITCOIN_CORE_H = \
darksend-relay.h \
governance.h \
governance-classes.h \
governance-exceptions.h \
governance-vote.h \
governance-votedb.h \
flat-database.h \
hash.h \
httprpc.h \
@ -202,6 +206,7 @@ libbitcoin_server_a_SOURCES = \
governance.cpp \
governance-classes.cpp \
governance-vote.cpp \
governance-votedb.cpp \
main.cpp \
merkleblock.cpp \
miner.cpp \

View File

@ -43,6 +43,8 @@ BITCOIN_TESTS =\
test/base64_tests.cpp \
test/bip32_tests.cpp \
test/bloom_tests.cpp \
test/cachemap_tests.cpp \
test/cachemultimap_tests.cpp \
test/checkblock_tests.cpp \
test/Checkpoints_tests.cpp \
test/coins_tests.cpp \

View File

@ -271,6 +271,7 @@ void CActiveMasternode::ManageStateLocal()
//update to masternode list
LogPrintf("CActiveMasternode::ManageStateLocal -- Update Masternode List\n");
mnodeman.UpdateMasternodeList(mnb);
mnodeman.NotifyMasternodeUpdates();
//send to all peers
LogPrintf("CActiveMasternode::ManageStateLocal -- Relay broadcast, vin=%s\n", vin.ToString());

202
src/cachemap.h Normal file
View File

@ -0,0 +1,202 @@
// Copyright (c) 2014-2016 The Dash Core developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef CACHEMAP_H_
#define CACHEMAP_H_
#include <map>
#include <list>
#include <cstddef>
#include "serialize.h"
/**
* Serializable structure for key/value items
*/
template<typename K, typename V>
struct CacheItem
{
CacheItem()
{}
CacheItem(const K& keyIn, const V& valueIn)
: key(keyIn),
value(valueIn)
{}
K key;
V value;
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
{
READWRITE(key);
READWRITE(value);
}
};
/**
* Map like container that keeps the N most recently added items
*/
template<typename K, typename V, typename Size = uint32_t>
class CacheMap
{
public:
typedef Size size_type;
typedef CacheItem<K,V> item_t;
typedef std::list<item_t> list_t;
typedef typename list_t::iterator list_it;
typedef typename list_t::const_iterator list_cit;
typedef std::map<K, list_it> map_t;
typedef typename map_t::iterator map_it;
typedef typename map_t::const_iterator map_cit;
private:
size_type nMaxSize;
size_type nCurrentSize;
list_t listItems;
map_t mapIndex;
public:
CacheMap(size_type nMaxSizeIn = 0)
: nMaxSize(nMaxSizeIn),
nCurrentSize(0),
listItems(),
mapIndex()
{}
CacheMap(const CacheMap<K,V>& other)
: nMaxSize(other.nMaxSize),
nCurrentSize(other.nCurrentSize),
listItems(other.listItems),
mapIndex()
{
RebuildIndex();
}
void Clear()
{
mapIndex.clear();
listItems.clear();
nCurrentSize = 0;
}
void SetMaxSize(size_type nMaxSizeIn)
{
nMaxSize = nMaxSizeIn;
}
size_type GetMaxSize() const {
return nMaxSize;
}
size_type GetSize() const {
return nCurrentSize;
}
void Insert(const K& key, const V& value)
{
map_it it = mapIndex.find(key);
if(it != mapIndex.end()) {
item_t& item = *(it->second);
item.value = value;
return;
}
if(nCurrentSize == nMaxSize) {
PruneLast();
}
listItems.push_front(item_t(key, value));
mapIndex[key] = listItems.begin();
++nCurrentSize;
}
bool HasKey(const K& key) const
{
map_cit it = mapIndex.find(key);
return (it != mapIndex.end());
}
bool Get(const K& key, V& value) const
{
map_cit it = mapIndex.find(key);
if(it == mapIndex.end()) {
return false;
}
item_t& item = *(it->second);
value = item.value;
return true;
}
void Erase(const K& key)
{
map_it it = mapIndex.find(key);
if(it == mapIndex.end()) {
return;
}
listItems.erase(it->second);
mapIndex.erase(it);
--nCurrentSize;
}
const list_t& GetItemList() const {
return listItems;
}
CacheMap<K,V>& operator=(const CacheMap<K,V>& other)
{
nMaxSize = other.nMaxSize;
nCurrentSize = other.nCurrentSize;
listItems = other.listItems;
RebuildIndex();
return *this;
}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
{
READWRITE(nMaxSize);
READWRITE(nCurrentSize);
READWRITE(listItems);
if(ser_action.ForRead()) {
RebuildIndex();
}
}
private:
void PruneLast()
{
if(nCurrentSize < 1) {
return;
}
item_t& item = listItems.back();
mapIndex.erase(item.key);
listItems.pop_back();
--nCurrentSize;
}
void RebuildIndex()
{
mapIndex.clear();
for(list_it it = listItems.begin(); it != listItems.end(); ++it) {
mapIndex[it->key] = it;
}
}
};
#endif /* CACHEMAP_H_ */

254
src/cachemultimap.h Normal file
View File

@ -0,0 +1,254 @@
// Copyright (c) 2014-2016 The Dash Core developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef CACHEMULTIMAP_H_
#define CACHEMULTIMAP_H_
#include <cstddef>
#include <map>
#include <list>
#include <set>
#include "serialize.h"
#include "cachemap.h"
/**
* Map like container that keeps the N most recently added items
*/
template<typename K, typename V, typename Size = uint32_t>
class CacheMultiMap
{
public:
typedef Size size_type;
typedef CacheItem<K,V> item_t;
typedef std::list<item_t> list_t;
typedef typename list_t::iterator list_it;
typedef typename list_t::const_iterator list_cit;
typedef std::map<V,list_it> it_map_t;
typedef typename it_map_t::iterator it_map_it;
typedef typename it_map_t::const_iterator it_map_cit;
typedef std::map<K, it_map_t> map_t;
typedef typename map_t::iterator map_it;
typedef typename map_t::const_iterator map_cit;
private:
size_type nMaxSize;
size_type nCurrentSize;
list_t listItems;
map_t mapIndex;
public:
CacheMultiMap(size_type nMaxSizeIn = 0)
: nMaxSize(nMaxSizeIn),
nCurrentSize(0),
listItems(),
mapIndex()
{}
CacheMultiMap(const CacheMap<K,V>& other)
: nMaxSize(other.nMaxSize),
nCurrentSize(other.nCurrentSize),
listItems(other.listItems),
mapIndex()
{
RebuildIndex();
}
void Clear()
{
mapIndex.clear();
listItems.clear();
nCurrentSize = 0;
}
void SetMaxSize(size_type nMaxSizeIn)
{
nMaxSize = nMaxSizeIn;
}
size_type GetMaxSize() const {
return nMaxSize;
}
size_type GetSize() const {
return nCurrentSize;
}
void Insert(const K& key, const V& value)
{
if(nCurrentSize == nMaxSize) {
PruneLast();
}
map_it mit = mapIndex.find(key);
if(mit == mapIndex.end()) {
mit = mapIndex.insert(std::pair<K,it_map_t>(key, it_map_t())).first;
}
it_map_t& mapIt = mit->second;
if(mapIt.count(value) > 0) {
// Don't insert duplicates
return;
}
listItems.push_front(item_t(key, value));
list_it lit = listItems.begin();
mapIt[value] = lit;
++nCurrentSize;
}
bool HasKey(const K& key) const
{
map_cit it = mapIndex.find(key);
return (it != mapIndex.end());
}
bool Get(const K& key, V& value) const
{
map_cit it = mapIndex.find(key);
if(it == mapIndex.end()) {
return false;
}
const it_map_t& mapIt = it->second;
const item_t& item = *(mapIt.begin()->second);
value = item.value;
return true;
}
bool GetAll(const K& key, std::vector<V>& vecValues)
{
map_cit mit = mapIndex.find(key);
if(mit == mapIndex.end()) {
return false;
}
const it_map_t& mapIt = mit->second;
for(it_map_cit it = mapIt.begin(); it != mapIt.end(); ++it) {
const item_t& item = *(it->second);
vecValues.push_back(item.value);
}
return true;
}
void Erase(const K& key)
{
map_it mit = mapIndex.find(key);
if(mit == mapIndex.end()) {
return;
}
it_map_t& mapIt = mit->second;
for(it_map_it it = mapIt.begin(); it != mapIt.end(); ++it) {
listItems.erase(it->second);
--nCurrentSize;
}
mapIndex.erase(mit);
}
void Erase(const K& key, const V& value)
{
map_it mit = mapIndex.find(key);
if(mit == mapIndex.end()) {
return;
}
it_map_t& mapIt = mit->second;
it_map_it it = mapIt.find(value);
if(it == mapIt.end()) {
return;
}
listItems.erase(it->second);
--nCurrentSize;
mapIt.erase(it);
if(mapIt.size() < 1) {
mapIndex.erase(mit);
}
}
const list_t& GetItemList() const {
return listItems;
}
CacheMap<K,V>& operator=(const CacheMap<K,V>& other)
{
nMaxSize = other.nMaxSize;
nCurrentSize = other.nCurrentSize;
listItems = other.listItems;
RebuildIndex();
return *this;
}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
{
READWRITE(nMaxSize);
READWRITE(nCurrentSize);
READWRITE(listItems);
if(ser_action.ForRead()) {
RebuildIndex();
}
}
private:
void PruneLast()
{
if(nCurrentSize < 1) {
return;
}
list_it lit = listItems.end();
--lit;
item_t& item = *lit;
map_it mit = mapIndex.find(item.key);
if(mit != mapIndex.end()) {
it_map_t& mapIt = mit->second;
mapIt.erase(item.value);
if(mapIt.size() < 1) {
mapIndex.erase(item.key);
}
}
listItems.pop_back();
--nCurrentSize;
}
void RebuildIndex()
{
mapIndex.clear();
for(list_it lit = listItems.begin(); lit != listItems.end(); ++lit) {
item_t& item = *lit;
map_it mit = mapIndex.find(item.key);
if(mit == mapIndex.end()) {
mit = mapIndex.insert(std::pair<K,it_map_t>(item.key, it_map_t())).first;
}
it_map_t& mapIt = mit->second;
mapIt[item.value] = lit;
}
}
};
#endif /* CACHEMULTIMAP_H_ */

View File

@ -346,7 +346,7 @@ bool CSuperblockManager::IsSuperblockTriggered(int nBlockHeight)
// MAKE SURE THIS TRIGGER IS ACTIVE VIA FUNDING CACHE FLAG
if(pObj->fCachedFunding) {
if(pObj->IsSetCachedFunding()) {
LogPrint("gobject", "CSuperblockManager::IsSuperblockTriggered -- fCacheFunding = true, returning true\n");
DBG( cout << "IsSuperblockTriggered returning true" << endl; );
return true;
@ -506,7 +506,7 @@ CSuperblock(uint256& nHash)
DBG( cout << "CSuperblock Constructor pGovObj : "
<< pGovObj->GetDataAsString()
<< ", nObjectType = " << pGovObj->nObjectType
<< ", nObjectType = " << pGovObj->GetObjectType()
<< endl; );
if (pGovObj->GetObjectType() != GOVERNANCE_OBJECT_TRIGGER) {
@ -678,7 +678,7 @@ bool CSuperblock::IsValid(const CTransaction& txNew, int nBlockHeight, CAmount b
int nMinerPayments = nOutputs - nPayments;
LogPrint("gobject", "CSuperblock::IsValid nOutputs = %d, nPayments = %d, strData = %s\n",
nOutputs, nPayments, GetGovernanceObject()->strData);
nOutputs, nPayments, GetGovernanceObject()->GetDataAsHex());
// We require an exact match (including order) between the expected
// superblock payments and the payments actually in the block, after

View File

@ -1,8 +1,8 @@
// Copyright (c) 2014-2016 The Dash Core developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef GOVERANCE_CLASSES_H
#define GOVERANCE_CLASSES_H
#ifndef GOVERNANCE_CLASSES_H
#define GOVERNANCE_CLASSES_H
//#define ENABLE_DASH_DEBUG

View File

@ -0,0 +1,98 @@
// Copyright (c) 2014-2016 The Dash Core developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef GOVERNANCE_EXCEPTIONS_H
#define GOVERNANCE_EXCEPTIONS_H
#include <exception>
#include <string>
#include <iostream>
enum governance_exception_type_enum_t {
/// Default value, normally indicates no exception condition occurred
GOVERNANCE_EXCEPTION_NONE = 0,
/// Unusual condition requiring no caller action
GOVERNANCE_EXCEPTION_WARNING = 1,
/// Requested operation cannot be performed
GOVERNANCE_EXCEPTION_PERMANENT_ERROR = 2,
/// Requested operation not currently possible, may resubmit later
GOVERNANCE_EXCEPTION_TEMPORARY_ERROR = 3,
/// Unexpected error (ie. should not happen unless there is a bug in the code)
GOVERNANCE_EXCEPTION_INTERNAL_ERROR = 4
};
inline std::ostream& operator<<(std::ostream& os, governance_exception_type_enum_t eType)
{
switch(eType) {
case GOVERNANCE_EXCEPTION_NONE:
os << "GOVERNANCE_EXCEPTION_NONE";
break;
case GOVERNANCE_EXCEPTION_WARNING:
os << "GOVERNANCE_EXCEPTION_WARNING";
break;
case GOVERNANCE_EXCEPTION_PERMANENT_ERROR:
os << "GOVERNANCE_EXCEPTION_PERMANENT_ERROR";
break;
case GOVERNANCE_EXCEPTION_TEMPORARY_ERROR:
os << "GOVERNANCE_EXCEPTION_TEMPORARY_ERROR";
break;
case GOVERNANCE_EXCEPTION_INTERNAL_ERROR:
os << "GOVERNANCE_EXCEPTION_INTERNAL_ERROR";
break;
}
return os;
}
/**
* A class which encapsulates information about a governance exception condition
*
* Derives from std::exception so is suitable for throwing
* (ie. will be caught by a std::exception handler) but may also be used as a
* normal object.
*/
class CGovernanceException : public std::exception
{
private:
std::string strMessage;
governance_exception_type_enum_t eType;
int nNodePenalty;
public:
CGovernanceException(const std::string& strMessageIn = "",
governance_exception_type_enum_t eTypeIn = GOVERNANCE_EXCEPTION_NONE,
int nNodePenaltyIn = 0)
: strMessage(),
eType(eTypeIn),
nNodePenalty(nNodePenaltyIn)
{
std::ostringstream ostr;
ostr << eType << ":" << strMessageIn;
strMessage = ostr.str();
}
virtual ~CGovernanceException() throw() {}
virtual const char* what() const throw()
{
return strMessage.c_str();
}
const std::string& GetMessage() const
{
return strMessage;
}
governance_exception_type_enum_t GetType() const
{
return eType;
}
int GetNodePenalty() const {
return nNodePenalty;
}
};
#endif

View File

@ -2,8 +2,8 @@
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef GOVERANCE_MISC_H
#define GOVERANCE_MISC_H
#ifndef GOVERNANCE_MISC_H
#define GOVERNANCE_MISC_H
#include "main.h"
#include "governance.h"

View File

@ -233,7 +233,7 @@ CGovernanceVote::CGovernanceVote(CTxIn vinMasternodeIn, uint256 nParentHashIn, v
vchSig()
{}
void CGovernanceVote::Relay()
void CGovernanceVote::Relay() const
{
CInv inv(MSG_GOVERNANCE_OBJECT_VOTE, GetHash());
RelayInv(inv, PROTOCOL_VERSION);
@ -262,15 +262,15 @@ bool CGovernanceVote::Sign(CKey& keyMasternode, CPubKey& pubKeyMasternode)
return true;
}
bool CGovernanceVote::IsValid(bool fSignatureCheck)
bool CGovernanceVote::IsValid(bool fSignatureCheck) const
{
if(nTime > GetTime() + (60*60)){
if(nTime > GetTime() + (60*60)) {
LogPrint("gobject", "CGovernanceVote::IsValid -- vote is too far ahead of current time - %s - nTime %lli - Max Time %lli\n", GetHash().ToString(), nTime, GetTime() + (60*60));
return false;
}
// support up to 50 actions (implemented in sentinel)
if(nVoteSignal > 50)
if(nVoteSignal > MAX_SUPPORTED_VOTE_SIGNAL)
{
LogPrint("gobject", "CGovernanceVote::IsValid -- Client attempted to vote on invalid signal(%d) - %s\n", nVoteSignal, GetHash().ToString());
return false;
@ -302,3 +302,44 @@ bool CGovernanceVote::IsValid(bool fSignatureCheck)
return true;
}
bool operator==(const CGovernanceVote& vote1, const CGovernanceVote& vote2)
{
bool fResult = ((vote1.vinMasternode == vote2.vinMasternode) &&
(vote1.nParentHash == vote2.nParentHash) &&
(vote1.nVoteOutcome == vote2.nVoteOutcome) &&
(vote1.nVoteSignal == vote2.nVoteSignal) &&
(vote1.nTime == vote2.nTime));
return fResult;
}
bool operator<(const CGovernanceVote& vote1, const CGovernanceVote& vote2)
{
bool fResult = (vote1.vinMasternode < vote2.vinMasternode);
if(!fResult) {
return false;
}
fResult = (vote1.vinMasternode == vote2.vinMasternode);
fResult = fResult && (vote1.nParentHash < vote2.nParentHash);
if(!fResult) {
return false;
}
fResult = fResult && (vote1.nParentHash == vote2.nParentHash);
fResult = fResult && (vote1.nVoteOutcome < vote2.nVoteOutcome);
if(!fResult) {
return false;
}
fResult = fResult && (vote1.nVoteOutcome == vote2.nVoteOutcome);
fResult = fResult && (vote1.nVoteSignal == vote2.nVoteSignal);
if(!fResult) {
return false;
}
fResult = fResult && (vote1.nVoteSignal == vote2.nVoteSignal);
fResult = fResult && (vote1.nTime < vote2.nTime);
return fResult;
}

View File

@ -2,8 +2,8 @@
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef GOVERANCE_VOTE_H
#define GOVERANCE_VOTE_H
#ifndef GOVERNANCE_VOTE_H
#define GOVERNANCE_VOTE_H
#include "main.h"
#include "sync.h"
@ -68,6 +68,8 @@ enum vote_signal_enum_t {
VOTE_SIGNAL_CUSTOM20 = 35
};
static const int MAX_SUPPORTED_VOTE_SIGNAL = VOTE_SIGNAL_ENDORSED;
/**
* Governance Voting
*
@ -89,6 +91,10 @@ public:
class CGovernanceVote
{
friend bool operator==(const CGovernanceVote& vote1, const CGovernanceVote& vote2);
friend bool operator<(const CGovernanceVote& vote1, const CGovernanceVote& vote2);
private:
bool fValid; //if the vote is currently valid / counted
bool fSynced; //if we've sent this to our peers
@ -120,8 +126,12 @@ public:
void SetSignature(const std::vector<unsigned char>& vchSigIn) { vchSig = vchSigIn; }
bool Sign(CKey& keyMasternode, CPubKey& pubKeyMasternode);
bool IsValid(bool fSignatureCheck);
void Relay();
bool IsValid(bool fSignatureCheck) const;
void Relay() const;
std::string GetVoteString() const {
return CGovernanceVoting::ConvertOutcomeToString(GetOutcome());
}
CTxIn& GetVinMasternode() { return vinMasternode; }
@ -144,7 +154,7 @@ public:
return ss.GetHash();
}
std::string ToString()
std::string ToString() const
{
std::ostringstream ostr;
ostr << vinMasternode.ToString() << ":"
@ -197,6 +207,7 @@ public:
};
/**
* 12.1.1 - CGovernanceVoteManager
* -------------------------------

84
src/governance-votedb.cpp Normal file
View File

@ -0,0 +1,84 @@
// Copyright (c) 2014-2016 The Dash Core developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "governance-votedb.h"
CGovernanceObjectVoteFile::CGovernanceObjectVoteFile()
: nMemoryVotes(0),
listVotes(),
mapVoteIndex()
{}
CGovernanceObjectVoteFile::CGovernanceObjectVoteFile(const CGovernanceObjectVoteFile& other)
: nMemoryVotes(other.nMemoryVotes),
listVotes(other.listVotes),
mapVoteIndex()
{
RebuildIndex();
}
void CGovernanceObjectVoteFile::AddVote(const CGovernanceVote& vote)
{
listVotes.push_front(vote);
mapVoteIndex[vote.GetHash()] = listVotes.begin();
++nMemoryVotes;
}
bool CGovernanceObjectVoteFile::HasVote(const uint256& nHash) const
{
vote_m_cit it = mapVoteIndex.find(nHash);
if(it == mapVoteIndex.end()) {
return false;
}
return true;
}
bool CGovernanceObjectVoteFile::GetVote(const uint256& nHash, CGovernanceVote& vote) const
{
vote_m_cit it = mapVoteIndex.find(nHash);
if(it == mapVoteIndex.end()) {
return false;
}
vote = *(it->second);
return true;
}
std::vector<CGovernanceVote> CGovernanceObjectVoteFile::GetVotes() const
{
std::vector<CGovernanceVote> vecResult;
for(vote_l_cit it = listVotes.begin(); it != listVotes.end(); ++it) {
vecResult.push_back(*it);
}
return vecResult;
}
void CGovernanceObjectVoteFile::RemoveVotesFromMasternode(const CTxIn& vinMasternode)
{
vote_l_it it = listVotes.begin();
while(it != listVotes.end()) {
if(it->GetVinMasternode() == vinMasternode) {
listVotes.erase(it++);
}
else {
++it;
}
}
}
CGovernanceObjectVoteFile& CGovernanceObjectVoteFile::operator=(const CGovernanceObjectVoteFile& other)
{
nMemoryVotes = other.nMemoryVotes;
listVotes = other.listVotes;
RebuildIndex();
return *this;
}
void CGovernanceObjectVoteFile::RebuildIndex()
{
mapVoteIndex.clear();
for(vote_l_it it = listVotes.begin(); it != listVotes.end(); ++it) {
CGovernanceVote& vote = *it;
mapVoteIndex[vote.GetHash()] = it;
}
}

93
src/governance-votedb.h Normal file
View File

@ -0,0 +1,93 @@
// Copyright (c) 2014-2016 The Dash Core developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef GOVERNANCE_VOTEDB_H
#define GOVERNANCE_VOTEDB_H
#include <list>
#include <map>
#include "governance-vote.h"
#include "serialize.h"
#include "uint256.h"
/**
* Represents the collection of votes associated with a given CGovernanceObject
* Recently received votes are held in memory until a maximum size is reached after
* which older votes a flushed to a disk file.
*
* Note: This is a stub implementation that doesn't limit the number of votes held
* in memory and doesn't flush to disk.
*/
class CGovernanceObjectVoteFile
{
public: // Types
typedef std::list<CGovernanceVote> vote_l_t;
typedef vote_l_t::iterator vote_l_it;
typedef vote_l_t::const_iterator vote_l_cit;
typedef std::map<uint256,vote_l_it> vote_m_t;
typedef vote_m_t::iterator vote_m_it;
typedef vote_m_t::const_iterator vote_m_cit;
private:
static const int MAX_MEMORY_VOTES = -1;
int nMemoryVotes;
vote_l_t listVotes;
vote_m_t mapVoteIndex;
public:
CGovernanceObjectVoteFile();
CGovernanceObjectVoteFile(const CGovernanceObjectVoteFile& other);
/**
* Add a vote to the file
*/
void AddVote(const CGovernanceVote& vote);
/**
* Return true if the vote with this hash is currently cached in memory
*/
bool HasVote(const uint256& nHash) const;
/**
* Retrieve a vote cached in memory
*/
bool GetVote(const uint256& nHash, CGovernanceVote& vote) const;
int GetVoteCount() {
return nMemoryVotes;
}
std::vector<CGovernanceVote> GetVotes() const;
CGovernanceObjectVoteFile& operator=(const CGovernanceObjectVoteFile& other);
void RemoveVotesFromMasternode(const CTxIn& vinMasternode);
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
{
READWRITE(nMemoryVotes);
READWRITE(listVotes);
if(ser_action.ForRead()) {
RebuildIndex();
}
}
private:
void RebuildIndex();
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -2,8 +2,8 @@
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef GOVERANCE_H
#define GOVERANCE_H
#ifndef GOVERNANCE_H
#define GOVERNANCE_H
//#define ENABLE_DASH_DEBUG
@ -15,17 +15,22 @@
#include "util.h"
#include "base58.h"
#include "masternode.h"
#include "governance-exceptions.h"
#include "governance-vote.h"
#include "governance-votedb.h"
#include "masternodeman.h"
#include <boost/lexical_cast.hpp>
#include "init.h"
#include <univalue.h>
#include "utilstrencodings.h"
#include "cachemap.h"
#include "cachemultimap.h"
#include <stdio.h>
#include <string.h>
class CGovernanceManager;
class CGovernanceTriggerManager;
class CGovernanceObject;
class CGovernanceVote;
@ -57,6 +62,8 @@ extern CGovernanceManager governance;
//
class CGovernanceManager
{
friend class CGovernanceObject;
public: // Types
typedef std::map<uint256, CGovernanceObject> object_m_t;
@ -65,6 +72,8 @@ public: // Types
typedef object_m_t::const_iterator object_m_cit;
typedef CacheMap<uint256, CGovernanceObject*> object_ref_cache_t;
typedef std::map<uint256, int> count_m_t;
typedef count_m_t::iterator count_m_it;
@ -77,11 +86,9 @@ public: // Types
typedef vote_m_t::const_iterator vote_m_cit;
typedef std::map<uint256, CTransaction> transaction_m_t;
typedef CacheMap<uint256, CGovernanceVote> vote_cache_t;
typedef transaction_m_t::iterator transaction_m_it;
typedef transaction_m_t::const_iterator transaction_m_cit;
typedef CacheMultiMap<uint256, CGovernanceVote> vote_mcache_t;
typedef object_m_t::size_type size_type;
@ -91,10 +98,17 @@ public: // Types
typedef txout_m_t::const_iterator txout_m_cit;
private:
typedef std::set<uint256> hash_s_t;
typedef hash_s_t::iterator hash_s_it;
typedef hash_s_t::const_iterator hash_s_cit;
private:
static const int MAX_CACHE_SIZE = 1000000;
static const std::string SERIALIZATION_VERSION_STRING;
//hold txes until they mature enough to use
transaction_m_t mapCollateral;
// Keep track of current block index
const CBlockIndex *pCurrentBlockIndex;
@ -105,57 +119,68 @@ private:
object_m_t mapObjects;
count_m_t mapSeenGovernanceObjects;
count_m_t mapSeenVotes;
vote_m_t mapOrphanVotes;
// todo: one of these should point to the other
// -- must be carefully managed while adding/removing/updating
vote_m_t mapVotesByHash;
vote_m_t mapVotesByType;
object_ref_cache_t mapVoteToObject;
vote_cache_t mapInvalidVotes;
vote_mcache_t mapOrphanVotes;
txout_m_t mapLastMasternodeTrigger;
hash_s_t setRequestedObjects;
hash_s_t setRequestedVotes;
public:
// critical section to protect the inner data structures
mutable CCriticalSection cs;
CGovernanceManager();
virtual ~CGovernanceManager() {}
void ClearSeen()
{
LOCK(cs);
mapSeenGovernanceObjects.clear();
mapSeenVotes.clear();
}
int CountProposalInventoryItems()
{
return mapSeenGovernanceObjects.size() + mapSeenVotes.size();
// TODO What is this for ?
return mapSeenGovernanceObjects.size();
//return mapSeenGovernanceObjects.size() + mapSeenVotes.size();
}
/**
* This is called by AlreadyHave in main.cpp as part of the inventory
* retrieval process. Returns true if we want to retrieve the object, otherwise
* false. (Note logic is inverted in AlreadyHave).
*/
bool ConfirmInventoryRequest(const CInv& inv);
void Sync(CNode* node, uint256 nProp);
void SyncParentObjectByVote(CNode* pfrom, const CGovernanceVote& vote);
void ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv);
void NewBlock();
CGovernanceObject *FindGovernanceObject(const uint256& nHash);
std::vector<CGovernanceVote*> GetMatchingVotes(const uint256& nParentHash);
std::vector<CGovernanceVote> GetMatchingVotes(const uint256& nParentHash);
std::vector<CGovernanceObject*> GetAllNewerThan(int64_t nMoreThanTime);
int CountMatchingVotes(CGovernanceObject& govobj, vote_signal_enum_t nVoteSignalIn, vote_outcome_enum_t nVoteOutcomeIn);
bool IsBudgetPaymentBlock(int nBlockHeight);
bool AddGovernanceObject (CGovernanceObject& govobj);
bool AddOrUpdateVote(const CGovernanceVote& vote, CNode* pfrom, std::string& strError);
std::string GetRequiredPaymentsString(int nBlockHeight);
void CleanAndRemove(bool fSignatureCheck);
void UpdateCachesAndClean();
void CheckAndRemove() {UpdateCachesAndClean();}
void CheckOrphanVotes();
void UpdateCachesAndClean();
void CheckAndRemove() {UpdateCachesAndClean();}
void Clear()
{
@ -164,10 +189,9 @@ public:
LogPrint("gobject", "Governance object manager was cleared\n");
mapObjects.clear();
mapSeenGovernanceObjects.clear();
mapSeenVotes.clear();
mapOrphanVotes.clear();
mapVotesByType.clear();
mapVotesByHash.clear();
mapVoteToObject.Clear();
mapInvalidVotes.Clear();
mapOrphanVotes.Clear();
mapLastMasternodeTrigger.clear();
}
@ -178,14 +202,25 @@ public:
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
LOCK(cs);
std::string strVersion;
if(ser_action.ForRead()) {
READWRITE(strVersion);
}
else {
strVersion = SERIALIZATION_VERSION_STRING;
READWRITE(strVersion);
}
READWRITE(mapSeenGovernanceObjects);
READWRITE(mapSeenVotes);
READWRITE(mapInvalidVotes);
READWRITE(mapOrphanVotes);
READWRITE(mapObjects);
READWRITE(mapVotesByHash);
READWRITE(mapVotesByType);
READWRITE(mapLastMasternodeTrigger);
if(ser_action.ForRead() && (strVersion != SERIALIZATION_VERSION_STRING)) {
Clear();
return;
}
if(ser_action.ForRead()) {
RebuildIndexes();
AddCachedTriggers();
}
}
@ -211,11 +246,90 @@ public:
bool MasternodeRateCheck(const CTxIn& vin, int nObjectType);
bool ProcessVote(const CGovernanceVote& vote, CGovernanceException& exception) {
return ProcessVote(NULL, vote, exception);
}
void CheckMasternodeOrphanVotes();
private:
void RequestGovernanceObject(CNode* pfrom, const uint256& nHash);
void AddInvalidVote(const CGovernanceVote& vote)
{
mapInvalidVotes.Insert(vote.GetHash(), vote);
}
void AddOrphanVote(const CGovernanceVote& vote)
{
mapOrphanVotes.Insert(vote.GetHash(), vote);
}
bool ProcessVote(CNode* pfrom, const CGovernanceVote& vote, CGovernanceException& exception);
/// Called to indicate a requested object has been received
bool AcceptObjectMessage(const uint256& nHash);
/// Called to indicate a requested vote has been received
bool AcceptVoteMessage(const uint256& nHash);
static bool AcceptMessage(const uint256& nHash, hash_s_t& setHash);
void CheckOrphanVotes(CNode* pfrom, CGovernanceObject& govobj, CGovernanceException& exception);
void RebuildIndexes();
/// Returns MN index, handling the case of index rebuilds
int GetMasternodeIndex(const CTxIn& masternodeVin);
void RebuildVoteMaps();
void AddCachedTriggers();
};
struct vote_instance_t {
vote_outcome_enum_t eOutcome;
int64_t nTime;
vote_instance_t(vote_outcome_enum_t eOutcomeIn = VOTE_OUTCOME_NONE, int64_t nTimeIn = 0)
: eOutcome(eOutcomeIn),
nTime(nTimeIn)
{}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
{
int nOutcome = int(eOutcome);
READWRITE(nOutcome);
READWRITE(nTime);
if(ser_action.ForRead()) {
eOutcome = vote_outcome_enum_t(nOutcome);
}
}
};
typedef std::map<int,vote_instance_t> vote_instance_m_t;
typedef vote_instance_m_t::iterator vote_instance_m_it;
typedef vote_instance_m_t::const_iterator vote_instance_m_cit;
struct vote_rec_t {
vote_instance_m_t mapInstances;
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
{
READWRITE(mapInstances);
}
};
/**
* Governance Object
*
@ -223,41 +337,140 @@ private:
class CGovernanceObject
{
friend class CGovernanceManager;
friend class CGovernanceTriggerManager;
public: // Types
typedef std::map<int, vote_rec_t> vote_m_t;
typedef vote_m_t::iterator vote_m_it;
typedef vote_m_t::const_iterator vote_m_cit;
typedef CacheMultiMap<CTxIn, CGovernanceVote> vote_mcache_t;
private:
// critical section to protect the inner data structures
/// critical section to protect the inner data structures
mutable CCriticalSection cs;
public:
uint256 nHashParent; //parent object, 0 is root
int nRevision; //object revision in the system
int64_t nTime; //time this object was created
uint256 nCollateralHash; //fee-tx
std::string strData; // Data field - can be used for anything
/// Object typecode
int nObjectType;
// Masternode info for signed objects
/// parent object, 0 is root
uint256 nHashParent;
/// object revision in the system
int nRevision;
/// time this object was created
int64_t nTime;
/// fee-tx
uint256 nCollateralHash;
/// Data field - can be used for anything
std::string strData;
/// Masternode info for signed objects
CTxIn vinMasternode;
std::vector<unsigned char> vchSig;
bool fCachedLocalValidity; // is valid by blockchain
/// is valid by blockchain
bool fCachedLocalValidity;
std::string strLocalValidityError;
// VARIOUS FLAGS FOR OBJECT / SET VIA MASTERNODE VOTING
bool fCachedFunding; // true == minimum network support has been reached for this object to be funded (doesn't mean it will for sure though)
bool fCachedValid; // true == minimum network has been reached flagging this object as a valid and understood goverance object (e.g, the serialized data is correct format, etc)
bool fCachedDelete; // true == minimum network support has been reached saying this object should be deleted from the system entirely
bool fCachedEndorsed; // true == minimum network support has been reached flagging this object as endorsed by an elected representative body (e.g. business review board / technecial review board /etc)
bool fDirtyCache; // object was updated and cached values should be updated soon
bool fUnparsable; // data field was unparsible, object will be rejected
bool fExpired; // Object is no longer of interest
/// true == minimum network support has been reached for this object to be funded (doesn't mean it will for sure though)
bool fCachedFunding;
/// true == minimum network has been reached flagging this object as a valid and understood goverance object (e.g, the serialized data is correct format, etc)
bool fCachedValid;
/// true == minimum network support has been reached saying this object should be deleted from the system entirely
bool fCachedDelete;
/** true == minimum network support has been reached flagging this object as endorsed by an elected representative body
* (e.g. business review board / technecial review board /etc)
*/
bool fCachedEndorsed;
/// object was updated and cached values should be updated soon
bool fDirtyCache;
/// Object is no longer of interest
bool fExpired;
/// Failed to parse object data
bool fUnparsable;
vote_m_t mapCurrentMNVotes;
/// Limited map of votes orphaned by MN
vote_mcache_t mapOrphanVotes;
CGovernanceObjectVoteFile fileVotes;
public:
CGovernanceObject();
CGovernanceObject(uint256 nHashParentIn, int nRevisionIn, int64_t nTime, uint256 nCollateralHashIn, std::string strDataIn);
CGovernanceObject(const CGovernanceObject& other);
void swap(CGovernanceObject& first, CGovernanceObject& second); // nothrow
// Public Getter methods
int64_t GetCreationTime() const {
return nTime;
}
int GetObjectType() const {
return nObjectType;
}
const uint256& GetCollateralHash() const {
return nCollateralHash;
}
const CTxIn& GetMasternodeVin() const {
return vinMasternode;
}
bool IsSetCachedFunding() const {
return fCachedFunding;
}
bool IsSetCachedValid() const {
return fCachedValid;
}
bool IsSetCachedDelete() const {
return fCachedDelete;
}
bool IsSetCachedEndorsed() const {
return fCachedEndorsed;
}
bool IsSetDirtyCache() const {
return fDirtyCache;
}
bool IsSetExpired() const {
return fExpired;
}
void InvalidateVoteCache() {
fDirtyCache = true;
}
CGovernanceObjectVoteFile& GetVoteFile() {
return fileVotes;
}
// Signature related functions
void SetMasternodeInfo(const CTxIn& vin);
@ -272,8 +485,9 @@ public:
bool IsCollateralValid(std::string& strError);
void UpdateLocalValidity(const CBlockIndex *pCurrentBlockIndex);
void UpdateSentinelVariables(const CBlockIndex *pCurrentBlockIndex);
int GetObjectType();
int GetObjectSubtype();
CAmount GetMinCollateralFee();
@ -281,15 +495,18 @@ public:
UniValue GetJSONObject();
void Relay();
uint256 GetHash();
// GET VOTE COUNT FOR SIGNAL
int GetAbsoluteYesCount(vote_signal_enum_t eVoteSignalIn);
int GetAbsoluteNoCount(vote_signal_enum_t eVoteSignalIn);
int GetYesCount(vote_signal_enum_t eVoteSignalIn);
int GetNoCount(vote_signal_enum_t eVoteSignalIn);
int GetAbstainCount(vote_signal_enum_t eVoteSignalIn);
int CountMatchingVotes(vote_signal_enum_t eVoteSignalIn, vote_outcome_enum_t eVoteOutcomeIn) const;
int GetAbsoluteYesCount(vote_signal_enum_t eVoteSignalIn) const;
int GetAbsoluteNoCount(vote_signal_enum_t eVoteSignalIn) const;
int GetYesCount(vote_signal_enum_t eVoteSignalIn) const;
int GetNoCount(vote_signal_enum_t eVoteSignalIn) const;
int GetAbstainCount(vote_signal_enum_t eVoteSignalIn) const;
// FUNCTIONS FOR DEALING WITH DATA STRING
@ -313,16 +530,33 @@ public:
READWRITE(nObjectType);
READWRITE(vinMasternode);
READWRITE(vchSig);
if(nType & SER_DISK) {
// Only include these for the disk file format
LogPrint("gobject", "CGovernanceObject::SerializationOp Reading/writing votes from/to disk\n");
READWRITE(mapCurrentMNVotes);
READWRITE(fileVotes);
LogPrint("gobject", "CGovernanceObject::SerializationOp hash = %s, vote count = %d\n", GetHash().ToString(), fileVotes.GetVoteCount());
}
// AFTER DESERIALIZATION OCCURS, CACHED VARIABLES MUST BE CALCULATED MANUALLY
}
private:
// FUNCTIONS FOR DEALING WITH DATA STRING
void LoadData();
void GetData(UniValue& objResult);
bool ProcessVote(CNode* pfrom,
const CGovernanceVote& vote,
CGovernanceException& exception);
void RebuildVoteMap();
/// Called when MN's which have voted on this object have been removed
void ClearMasternodeVotes();
void CheckOrphanVotes();
};

View File

@ -4948,14 +4948,11 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
return mapDarksendBroadcastTxes.count(inv.hash);
case MSG_GOVERNANCE_OBJECT:
return governance.HaveObjectForHash(inv.hash);
case MSG_GOVERNANCE_OBJECT_VOTE:
return governance.HaveVoteForHash(inv.hash);
return ! governance.ConfirmInventoryRequest(inv);
case MSG_MASTERNODE_VERIFY:
return mnodeman.mapSeenMasternodeVerification.count(inv.hash);
}
// Don't know what it is, just say we already got one
@ -6720,9 +6717,15 @@ bool SendMessages(CNode* pto)
//
// Message: getdata (non-blocks)
//
int64_t nFirst = -1;
if(!pto->mapAskFor.empty()) {
nFirst = (*pto->mapAskFor.begin()).first;
}
LogPrint("net", "SendMessages (mapAskFor) -- before loop: nNow = %d, nFirst = %d\n", nNow, nFirst);
while (!pto->fDisconnect && !pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow)
{
const CInv& inv = (*pto->mapAskFor.begin()).second;
LogPrint("net", "SendMessages (mapAskFor) -- inv = %s peer=%d\n", inv.ToString(), pto->id);
if (!AlreadyHave(inv))
{
if (fDebug)
@ -6735,6 +6738,7 @@ bool SendMessages(CNode* pto)
}
} else {
//If we're not going to ask, don't expect a response.
LogPrint("net", "SendMessages -- already have inv = %s peer=%d\n", inv.ToString(), pto->id);
pto->setAskFor.erase(inv.hash);
}
pto->mapAskFor.erase(pto->mapAskFor.begin());

View File

@ -882,6 +882,14 @@ void CMasternode::UpdateWatchdogVoteTime()
*/
void CMasternode::FlagGovernanceItemsAsDirty()
{
std::map<uint256, int>::iterator it = mapGovernanceObjectsVotedOn.begin();
while(it != mapGovernanceObjectsVotedOn.end()){
CGovernanceObject *pObj = governance.FindGovernanceObject((*it).first);
if(pObj) pObj->InvalidateVoteCache();
++it;
}
std::vector<uint256> vecDirty;
{
std::map<uint256, int>::iterator it = mapGovernanceObjectsVotedOn.begin();

View File

@ -5,6 +5,7 @@
#include "masternodeman.h"
#include "activemasternode.h"
#include "darksend.h"
#include "governance.h"
#include "masternode.h"
#include "masternode-payments.h"
#include "masternode-sync.h"
@ -38,7 +39,51 @@ struct CompareScoreMN
}
};
CMasternodeIndex::CMasternodeIndex()
: nSize(0),
mapIndex(),
mapReverseIndex()
{}
bool CMasternodeIndex::Get(int nIndex, CTxIn& vinMasternode) const
{
rindex_m_cit it = mapReverseIndex.find(nIndex);
if(it == mapReverseIndex.end()) {
return false;
}
vinMasternode = it->second;
return true;
}
int CMasternodeIndex::GetMasternodeIndex(const CTxIn& vinMasternode) const
{
index_m_cit it = mapIndex.find(vinMasternode);
if(it == mapIndex.end()) {
return -1;
}
return it->second;
}
void CMasternodeIndex::AddMasternodeVIN(const CTxIn& vinMasternode)
{
index_m_it it = mapIndex.find(vinMasternode);
if(it != mapIndex.end()) {
return;
}
int nNextIndex = nSize;
mapIndex[vinMasternode] = nNextIndex;
mapReverseIndex[nNextIndex] = vinMasternode;
++nSize;
}
void CMasternodeIndex::Clear()
{
mapIndex.clear();
mapReverseIndex.clear();
nSize = 0;
}
struct CompareByAddr
{
bool operator()(const CMasternode* t1,
const CMasternode* t2) const
@ -47,6 +92,33 @@ struct CompareByAddr
}
};
void CMasternodeIndex::RebuildIndex()
{
nSize = mapIndex.size();
for(index_m_it it = mapIndex.begin(); it != mapIndex.end(); ++it) {
mapReverseIndex[it->second] = it->first;
}
}
CMasternodeMan::CMasternodeMan()
: cs(),
vMasternodes(),
mAskedUsForMasternodeList(),
mWeAskedForMasternodeList(),
mWeAskedForMasternodeListEntry(),
nLastIndexRebuildTime(0),
indexMasternodes(),
indexMasternodesOld(),
fIndexRebuilt(false),
fMasternodesAdded(false),
fMasternodesRemoved(false),
vecDirtyGovernanceObjectHashes(),
nLastWatchdogVoteTime(0),
mapSeenMasternodeBroadcast(),
mapSeenMasternodePing(),
nDsqCount(0)
{}
bool CMasternodeMan::Add(CMasternode &mn)
{
LOCK(cs);
@ -59,13 +131,15 @@ bool CMasternodeMan::Add(CMasternode &mn)
LogPrint("masternode", "CMasternodeMan::Add -- Adding new Masternode: addr=%s, %i now\n", mn.addr.ToString(), size() + 1);
mn.nTimeLastWatchdogVote = mn.sigTime;
vMasternodes.push_back(mn);
indexMasternodes.AddMasternodeVIN(mn.vin);
fMasternodesAdded = true;
return true;
}
return false;
}
void CMasternodeMan::AskForMN(CNode* pnode, CTxIn &vin)
void CMasternodeMan::AskForMN(CNode* pnode, const CTxIn &vin)
{
if(!pnode) return;
@ -99,6 +173,7 @@ void CMasternodeMan::CheckAndRemove(bool fForceExpiredRemoval)
Check();
{
LOCK(cs);
// Remove inactive and outdated masternodes
@ -121,6 +196,7 @@ void CMasternodeMan::CheckAndRemove(bool fForceExpiredRemoval)
// and finally remove it from the list
it = vMasternodes.erase(it);
fMasternodesRemoved = true;
} else {
++it;
}
@ -199,6 +275,15 @@ void CMasternodeMan::CheckAndRemove(bool fForceExpiredRemoval)
}
LogPrintf("CMasternodeMan::CheckAndRemove -- %s\n", ToString());
if(fMasternodesRemoved) {
CheckAndRebuildMasternodeIndex();
}
}
if(fMasternodesRemoved) {
NotifyMasternodeUpdates();
}
}
void CMasternodeMan::Clear()
@ -610,6 +695,9 @@ void CMasternodeMan::ProcessMessage(CNode* pfrom, std::string& strCommand, CData
if (strCommand == NetMsgType::MNANNOUNCE) { //Masternode Broadcast
{
LOCK(cs);
CMasternodeBroadcast mnb;
vRecv >> mnb;
@ -621,9 +709,11 @@ void CMasternodeMan::ProcessMessage(CNode* pfrom, std::string& strCommand, CData
} else if(nDos > 0) {
Misbehaving(pfrom->GetId(), nDos);
}
}
if(fMasternodesAdded) {
NotifyMasternodeUpdates();
}
} else if (strCommand == NetMsgType::MNPING) { //Masternode Ping
// ignore masternode pings until masternode list is synced
if (!masternodeSync.IsMasternodeListSynced()) return;
@ -657,7 +747,6 @@ void CMasternodeMan::ProcessMessage(CNode* pfrom, std::string& strCommand, CData
AskForMN(pfrom, mnp.vin);
} else if (strCommand == NetMsgType::DSEG) { //Get Masternode list or specific entry
// Ignore such requests until we are fully synced.
// We could start processing this after masternode list is synced
// but this is a heavy one so it's better to finish sync first.
@ -722,6 +811,8 @@ void CMasternodeMan::ProcessMessage(CNode* pfrom, std::string& strCommand, CData
} else if (strCommand == NetMsgType::MNVERIFY) { // Masternode Verify
LOCK(cs);
CMasternodeVerification mnv;
vRecv >> mnv;
@ -1290,6 +1381,32 @@ void CMasternodeMan::UpdateLastPaid(const CBlockIndex *pindex)
IsFirstRun = !masternodeSync.IsWinnersListSynced();
}
void CMasternodeMan::CheckAndRebuildMasternodeIndex()
{
LOCK(cs);
if(GetTime() - nLastIndexRebuildTime < MIN_INDEX_REBUILD_TIME) {
return;
}
if(indexMasternodes.GetSize() <= MAX_EXPECTED_INDEX_SIZE) {
return;
}
if(indexMasternodes.GetSize() <= int(vMasternodes.size())) {
return;
}
indexMasternodesOld = indexMasternodes;
indexMasternodes.Clear();
for(size_t i = 0; i < vMasternodes.size(); ++i) {
indexMasternodes.AddMasternodeVIN(vMasternodes[i].vin);
}
fIndexRebuilt = true;
nLastIndexRebuildTime = GetTime();
}
void CMasternodeMan::UpdateWatchdogVoteTime(const CTxIn& vin)
{
LOCK(cs);
@ -1406,3 +1523,26 @@ void CMasternodeMan::UpdatedBlockTip(const CBlockIndex *pindex)
UpdateLastPaid(pindex);
}
}
void CMasternodeMan::NotifyMasternodeUpdates()
{
// Avoid double locking
bool fMasternodesAddedLocal = false;
bool fMasternodesRemovedLocal = false;
{
LOCK(cs);
fMasternodesAddedLocal = fMasternodesAdded;
fMasternodesRemovedLocal = fMasternodesRemoved;
}
if(fMasternodesAddedLocal) {
governance.CheckMasternodeOrphanVotes();
}
if(fMasternodesRemovedLocal) {
governance.UpdateCachesAndClean();
}
LOCK(cs);
fMasternodesAdded = false;
fMasternodesRemoved = false;
}

View File

@ -14,9 +14,85 @@ class CMasternodeMan;
extern CMasternodeMan mnodeman;
/**
* Provides a forward and reverse index between MN vin's and integers.
*
* This mapping is normally add-only and is expected to be permanent
* It is only rebuilt if the size of the index exceeds the expected maximum number
* of MN's and the current number of known MN's.
*
* The external interface to this index is provided via delegation by CMasternodeMan
*/
class CMasternodeIndex
{
public: // Types
typedef std::map<CTxIn,int> index_m_t;
typedef index_m_t::iterator index_m_it;
typedef index_m_t::const_iterator index_m_cit;
typedef std::map<int,CTxIn> rindex_m_t;
typedef rindex_m_t::iterator rindex_m_it;
typedef rindex_m_t::const_iterator rindex_m_cit;
private:
int nSize;
index_m_t mapIndex;
rindex_m_t mapReverseIndex;
public:
CMasternodeIndex();
int GetSize() const {
return nSize;
}
/// Retrieve masternode vin by index
bool Get(int nIndex, CTxIn& vinMasternode) const;
/// Get index of a masternode vin
int GetMasternodeIndex(const CTxIn& vinMasternode) const;
void AddMasternodeVIN(const CTxIn& vinMasternode);
void Clear();
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion)
{
READWRITE(mapIndex);
if(ser_action.ForRead()) {
RebuildIndex();
}
}
private:
void RebuildIndex();
};
class CMasternodeMan
{
public:
typedef std::map<CTxIn,int> index_m_t;
typedef index_m_t::iterator index_m_it;
typedef index_m_t::const_iterator index_m_cit;
private:
static const int MAX_EXPECTED_INDEX_SIZE = 30000;
/// Only allow 1 index rebuild per hour
static const int64_t MIN_INDEX_REBUILD_TIME = 3600;
static const std::string SERIALIZATION_VERSION_STRING;
static const int DSEG_UPDATE_SECONDS = 3 * 60 * 60;
@ -45,6 +121,21 @@ private:
// who we asked for the masternode verification
std::map<CNetAddr, CMasternodeVerification> mWeAskedForVerification;
int64_t nLastIndexRebuildTime;
CMasternodeIndex indexMasternodes;
CMasternodeIndex indexMasternodesOld;
/// Set when index has been rebuilt, clear when read
bool fIndexRebuilt;
/// Set when masternodes are added, cleared when CGovernanceManager is notified
bool fMasternodesAdded;
/// Set when masternodes are removed, cleared when CGovernanceManager is notified
bool fMasternodesRemoved;
std::vector<uint256> vecDirtyGovernanceObjectHashes;
int64_t nLastWatchdogVoteTime;
@ -62,8 +153,6 @@ public:
int64_t nDsqCount;
CMasternodeMan() : nLastWatchdogVoteTime(0), nDsqCount(0) {}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
@ -87,16 +176,19 @@ public:
READWRITE(mapSeenMasternodeBroadcast);
READWRITE(mapSeenMasternodePing);
READWRITE(indexMasternodes);
if(ser_action.ForRead() && (strVersion != SERIALIZATION_VERSION_STRING)) {
Clear();
}
}
CMasternodeMan();
/// Add an entry
bool Add(CMasternode &mn);
/// Ask (source) node for mnb
void AskForMN(CNode *pnode, CTxIn &vin);
void AskForMN(CNode *pnode, const CTxIn &vin);
/// Check all Masternodes
void Check();
@ -128,6 +220,49 @@ public:
bool Get(const CPubKey& pubKeyMasternode, CMasternode& masternode);
bool Get(const CTxIn& vin, CMasternode& masternode);
/// Retrieve masternode vin by index
bool Get(int nIndex, CTxIn& vinMasternode, bool& fIndexRebuiltOut) {
LOCK(cs);
fIndexRebuiltOut = fIndexRebuilt;
return indexMasternodes.Get(nIndex, vinMasternode);
}
bool GetIndexRebuiltFlag() {
LOCK(cs);
return fIndexRebuilt;
}
/// Get index of a masternode vin
int GetMasternodeIndex(const CTxIn& vinMasternode) {
LOCK(cs);
return indexMasternodes.GetMasternodeIndex(vinMasternode);
}
/// Get old index of a masternode vin
int GetMasternodeIndexOld(const CTxIn& vinMasternode) {
LOCK(cs);
return indexMasternodesOld.GetMasternodeIndex(vinMasternode);
}
/// Get masternode VIN for an old index value
bool GetMasternodeVinForIndexOld(int nMasternodeIndex, CTxIn& vinMasternodeOut) {
LOCK(cs);
return indexMasternodesOld.Get(nMasternodeIndex, vinMasternodeOut);
}
/// Get index of a masternode vin, returning rebuild flag
int GetMasternodeIndex(const CTxIn& vinMasternode, bool& fIndexRebuiltOut) {
LOCK(cs);
fIndexRebuiltOut = fIndexRebuilt;
return indexMasternodes.GetMasternodeIndex(vinMasternode);
}
void ClearOldMasternodeIndex() {
LOCK(cs);
indexMasternodesOld.Clear();
fIndexRebuilt = false;
}
bool Has(const CTxIn& vin);
masternode_info_t GetMasternodeInfo(const CTxIn& vin);
@ -173,6 +308,8 @@ public:
void UpdateLastPaid(const CBlockIndex *pindex);
void CheckAndRebuildMasternodeIndex();
void AddDirtyGovernanceObjectHash(const uint256& nHash)
{
LOCK(cs);
@ -202,6 +339,13 @@ public:
void SetMasternodeLastPing(const CTxIn& vin, const CMasternodePing& mnp);
void UpdatedBlockTip(const CBlockIndex *pindex);
/**
* Called to notify CGovernanceManager that the masternode index has been updated.
* Must be called while not holding the CMasternodeMan::cs mutex
*/
void NotifyMasternodeUpdates();
};
#endif

View File

@ -106,6 +106,7 @@ void MasternodeList::StartAlias(std::string strAlias)
strStatusHtml += "<br>Successfully started masternode.";
mnodeman.UpdateMasternodeList(mnb);
mnb.Relay();
mnodeman.NotifyMasternodeUpdates();
} else {
strStatusHtml += "<br>Failed to start masternode.<br>Error: " + strError;
}
@ -142,6 +143,7 @@ void MasternodeList::StartAll(std::string strCommand)
nCountSuccessful++;
mnodeman.UpdateMasternodeList(mnb);
mnb.Relay();
mnodeman.NotifyMasternodeUpdates();
} else {
nCountFailed++;
strFailedHtml += "\nFailed to start " + mne.getAlias() + ". Error: " + strError;

View File

@ -283,16 +283,15 @@ UniValue gobject(const UniValue& params, bool fHelp)
return returnObj;
}
std::string strError = "";
if(governance.AddOrUpdateVote(vote, NULL, strError)) {
governance.AddSeenVote(vote.GetHash(), SEEN_OBJECT_IS_VALID);
vote.Relay();
CGovernanceException exception;
if(governance.ProcessVote(vote, exception)) {
success++;
statusObj.push_back(Pair("result", "success"));
} else {
}
else {
failed++;
statusObj.push_back(Pair("result", "failed"));
statusObj.push_back(Pair("errorMessage", strError.c_str()));
statusObj.push_back(Pair("errorMessage", exception.GetMessage()));
}
resultsObj.push_back(Pair("dash.conf", statusObj));
@ -386,15 +385,15 @@ UniValue gobject(const UniValue& params, bool fHelp)
continue;
}
if(governance.AddOrUpdateVote(vote, NULL, strError)) {
governance.AddSeenVote(vote.GetHash(), SEEN_OBJECT_IS_VALID);
vote.Relay();
CGovernanceException exception;
if(governance.ProcessVote(vote, exception)) {
success++;
statusObj.push_back(Pair("result", "success"));
} else {
}
else {
failed++;
statusObj.push_back(Pair("result", "failed"));
statusObj.push_back(Pair("errorMessage", strError.c_str()));
statusObj.push_back(Pair("errorMessage", exception.GetMessage()));
}
resultsObj.push_back(Pair(mne.getAlias(), statusObj));
@ -511,15 +510,15 @@ UniValue gobject(const UniValue& params, bool fHelp)
// UPDATE LOCAL DATABASE WITH NEW OBJECT SETTINGS
if(governance.AddOrUpdateVote(vote, NULL, strError)) {
governance.AddSeenVote(vote.GetHash(), SEEN_OBJECT_IS_VALID);
vote.Relay();
CGovernanceException exception;
if(governance.ProcessVote(vote, exception)) {
success++;
statusObj.push_back(Pair("result", "success"));
} else {
}
else {
failed++;
statusObj.push_back(Pair("result", "failed"));
statusObj.push_back(Pair("errorMessage", strError.c_str()));
statusObj.push_back(Pair("errorMessage", exception.GetMessage()));
}
resultsObj.push_back(Pair(mne.getAlias(), statusObj));
@ -573,13 +572,13 @@ UniValue gobject(const UniValue& params, bool fHelp)
BOOST_FOREACH(CGovernanceObject* pGovObj, objs)
{
// IF WE HAVE A SPECIFIC NODE REQUESTED TO VOTE, DO THAT
if(strShow == "valid" && !pGovObj->fCachedValid) continue;
if(strShow == "valid" && !pGovObj->IsSetCachedValid()) continue;
UniValue bObj(UniValue::VOBJ);
bObj.push_back(Pair("DataHex", pGovObj->GetDataAsHex()));
bObj.push_back(Pair("DataString", pGovObj->GetDataAsString()));
bObj.push_back(Pair("Hash", pGovObj->GetHash().ToString()));
bObj.push_back(Pair("CollateralHash", pGovObj->nCollateralHash.ToString()));
bObj.push_back(Pair("CollateralHash", pGovObj->GetCollateralHash().ToString()));
// REPORT STATUS FOR FUNDING VOTES SPECIFICALLY
bObj.push_back(Pair("AbsoluteYesCount", pGovObj->GetAbsoluteYesCount(VOTE_SIGNAL_FUNDING)));
@ -591,10 +590,10 @@ UniValue gobject(const UniValue& params, bool fHelp)
std::string strError = "";
bObj.push_back(Pair("fBlockchainValidity", pGovObj->IsValidLocally(pindex , strError, false)));
bObj.push_back(Pair("IsValidReason", strError.c_str()));
bObj.push_back(Pair("fCachedValid", pGovObj->fCachedValid));
bObj.push_back(Pair("fCachedFunding", pGovObj->fCachedFunding));
bObj.push_back(Pair("fCachedDelete", pGovObj->fCachedDelete));
bObj.push_back(Pair("fCachedEndorsed", pGovObj->fCachedEndorsed));
bObj.push_back(Pair("fCachedValid", pGovObj->IsSetCachedValid()));
bObj.push_back(Pair("fCachedFunding", pGovObj->IsSetCachedFunding()));
bObj.push_back(Pair("fCachedDelete", pGovObj->IsSetCachedDelete()));
bObj.push_back(Pair("fCachedEndorsed", pGovObj->IsSetCachedEndorsed()));
objResult.push_back(Pair(pGovObj->GetHash().ToString(), bObj));
}
@ -625,7 +624,7 @@ UniValue gobject(const UniValue& params, bool fHelp)
objResult.push_back(Pair("DataHex", pGovObj->GetDataAsHex()));
objResult.push_back(Pair("DataString", pGovObj->GetDataAsString()));
objResult.push_back(Pair("Hash", pGovObj->GetHash().ToString()));
objResult.push_back(Pair("CollateralHash", pGovObj->nCollateralHash.ToString()));
objResult.push_back(Pair("CollateralHash", pGovObj->GetCollateralHash().ToString()));
// SHOW (MUCH MORE) INFORMATION ABOUT VOTES FOR GOVERNANCE OBJECT (THAN LIST/DIFF ABOVE)
// -- FUNDING VOTING RESULTS
@ -665,11 +664,10 @@ UniValue gobject(const UniValue& params, bool fHelp)
std::string strError = "";
objResult.push_back(Pair("fLocalValidity", pGovObj->IsValidLocally(chainActive.Tip(), strError, false)));
objResult.push_back(Pair("IsValidReason", strError.c_str()));
objResult.push_back(Pair("fCachedValid", pGovObj->fCachedValid));
objResult.push_back(Pair("fCachedFunding", pGovObj->fCachedFunding));
objResult.push_back(Pair("fCachedDelete", pGovObj->fCachedDelete));
objResult.push_back(Pair("fCachedEndorsed", pGovObj->fCachedEndorsed));
objResult.push_back(Pair("fCachedValid", pGovObj->IsSetCachedValid()));
objResult.push_back(Pair("fCachedFunding", pGovObj->IsSetCachedFunding()));
objResult.push_back(Pair("fCachedDelete", pGovObj->IsSetCachedDelete()));
objResult.push_back(Pair("fCachedEndorsed", pGovObj->IsSetCachedEndorsed()));
return objResult;
}
@ -701,9 +699,9 @@ UniValue gobject(const UniValue& params, bool fHelp)
// GET MATCHING VOTES BY HASH, THEN SHOW USERS VOTE INFORMATION
std::vector<CGovernanceVote*> vecVotes = governance.GetMatchingVotes(hash);
BOOST_FOREACH(CGovernanceVote* pVote, vecVotes) {
bResult.push_back(Pair(pVote->GetHash().ToString(), pVote->ToString()));
std::vector<CGovernanceVote> vecVotes = governance.GetMatchingVotes(hash);
BOOST_FOREACH(CGovernanceVote vote, vecVotes) {
bResult.push_back(Pair(vote.GetHash().ToString(), vote.ToString()));
}
return bResult;
@ -764,13 +762,12 @@ UniValue voteraw(const UniValue& params, bool fHelp)
throw JSONRPCError(RPC_INTERNAL_ERROR, "Failure to verify vote.");
}
std::string strError = "";
if(governance.AddOrUpdateVote(vote, NULL, strError)) {
governance.AddSeenVote(vote.GetHash(), SEEN_OBJECT_IS_VALID);
vote.Relay();
CGovernanceException exception;
if(governance.ProcessVote(vote, exception)) {
return "Voted successfully";
} else {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Error voting : " + strError);
}
else {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Error voting : " + exception.GetMessage());
}
}

View File

@ -272,6 +272,7 @@ UniValue masternode(const UniValue& params, bool fHelp)
} else {
statusObj.push_back(Pair("errorMessage", strError));
}
mnodeman.NotifyMasternodeUpdates();
break;
}
}
@ -328,6 +329,7 @@ UniValue masternode(const UniValue& params, bool fHelp)
resultsObj.push_back(Pair("status", statusObj));
}
mnodeman.NotifyMasternodeUpdates();
UniValue returnObj(UniValue::VOBJ);
returnObj.push_back(Pair("overall", strprintf("Successfully started %d masternodes, failed to start %d, total %d", nSuccessful, nFailed, nSuccessful + nFailed)));
@ -781,6 +783,7 @@ UniValue masternodebroadcast(const UniValue& params, bool fHelp)
mnb.Relay();
fResult = true;
}
mnodeman.NotifyMasternodeUpdates();
} else fResult = false;
if(fResult) {

View File

@ -12,6 +12,7 @@
#include <assert.h>
#include <ios>
#include <limits>
#include <list>
#include <map>
#include <set>
#include <stdint.h>
@ -886,6 +887,39 @@ void Unserialize(Stream& is, std::set<K, Pred, A>& m, int nType, int nVersion)
}
}
/**
* list
*/
template<typename T, typename A>
unsigned int GetSerializeSize(const std::list<T, A>& l, int nType, int nVersion)
{
unsigned int nSize = GetSizeOfCompactSize(l.size());
for (typename std::list<T, A>::const_iterator it = l.begin(); it != l.end(); ++it)
nSize += GetSerializeSize((*it), nType, nVersion);
return nSize;
}
template<typename Stream, typename T, typename A>
void Serialize(Stream& os, const std::list<T, A>& l, int nType, int nVersion)
{
WriteCompactSize(os, l.size());
for (typename std::list<T, A>::const_iterator it = l.begin(); it != l.end(); ++it)
Serialize(os, (*it), nType, nVersion);
}
template<typename Stream, typename T, typename A>
void Unserialize(Stream& is, std::list<T, A>& l, int nType, int nVersion)
{
l.clear();
unsigned int nSize = ReadCompactSize(is);
for (unsigned int i = 0; i < nSize; i++)
{
T val;
Unserialize(is, val, nType, nVersion);
l.push_back(val);
}
}
/**

127
src/test/cachemap_tests.cpp Normal file
View File

@ -0,0 +1,127 @@
// Copyright (c) 2014-2016 The Dash Core developers
#include "cachemap.h"
#include "test/test_dash.h"
#include <boost/test/unit_test.hpp>
BOOST_FIXTURE_TEST_SUITE(cachemap_tests, BasicTestingSetup)
bool Compare(const CacheMap<int,int>& map1, const CacheMap<int,int>& map2 )
{
if(map1.GetMaxSize() != map2.GetMaxSize()) {
return false;
}
if(map1.GetSize() != map2.GetSize()) {
return false;
}
const CacheMap<int,int>::list_t& items1 = map1.GetItemList();
for(CacheMap<int,int>::list_cit it = items1.begin(); it != items1.end(); ++it) {
if(!map2.HasKey(it->key)) {
return false;
}
int val = 0;
if(!map2.Get(it->key, val)) {
return false;
}
if(it->value != val) {
return false;
}
}
const CacheMap<int,int>::list_t& items2 = map2.GetItemList();
for(CacheMap<int,int>::list_cit it = items2.begin(); it != items2.end(); ++it) {
if(!map1.HasKey(it->key)) {
return false;
}
int val = 0;
if(!map1.Get(it->key, val)) {
return false;
}
if(it->value != val) {
return false;
}
}
return true;
}
BOOST_AUTO_TEST_CASE(cachemap_test)
{
// create a CacheMap limited to 10 items
CacheMap<int,int> mapTest1(10);
// check that the max size is 10
BOOST_CHECK(mapTest1.GetMaxSize() == 10);
// check that the size is 0
BOOST_CHECK(mapTest1.GetSize() == 0);
// insert (-1, -1)
mapTest1.Insert(-1, -1);
// make sure that the size is updated
BOOST_CHECK(mapTest1.GetSize() == 1);
// make sure the map contains the key
BOOST_CHECK(mapTest1.HasKey(-1) == true);
// add 10 items
for(int i = 0; i < 10; ++i) {
mapTest1.Insert(i, i);
}
// check that the size is 10
BOOST_CHECK(mapTest1.GetSize() == 10);
// check that the map contains the expected items
for(int i = 0; i < 10; ++i) {
int nVal = 0;
BOOST_CHECK(mapTest1.Get(i, nVal) == true);
BOOST_CHECK(nVal == i);
}
// check that the map no longer contains the first item
BOOST_CHECK(mapTest1.HasKey(-1) == false);
// erase an item
mapTest1.Erase(5);
// check the size
BOOST_CHECK(mapTest1.GetSize() == 9);
// check that the map no longer contains the item
BOOST_CHECK(mapTest1.HasKey(5) == false);
// check that the map contains the expected items
int expected[] = { 0, 1, 2, 3, 4, 6, 7, 8, 9 };
for(size_t i = 0; i < 9; ++i) {
int nVal = 0;
int eVal = expected[i];
BOOST_CHECK(mapTest1.Get(eVal, nVal) == true);
BOOST_CHECK(nVal == eVal);
}
// test serialization
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << mapTest1;
CacheMap<int,int> mapTest2;
ss >> mapTest2;
BOOST_CHECK(Compare(mapTest1, mapTest2));
// test copy constructor
CacheMap<int,int> mapTest3(mapTest1);
BOOST_CHECK(Compare(mapTest1, mapTest3));
// test assignment operator
CacheMap<int,int> mapTest4;
mapTest4 = mapTest1;
BOOST_CHECK(Compare(mapTest1, mapTest4));
}
BOOST_AUTO_TEST_SUITE_END()

View File

@ -0,0 +1,176 @@
// Copyright (c) 2014-2016 The Dash Core developers
#include "cachemultimap.h"
#include "test/test_dash.h"
#include <algorithm>
#include <iostream>
#include <boost/test/unit_test.hpp>
BOOST_FIXTURE_TEST_SUITE(cachemultimap_tests, BasicTestingSetup)
void DumpMap(const CacheMultiMap<int,int>& map)
{
const CacheMultiMap<int,int>::list_t& listItems = map.GetItemList();
for(CacheMultiMap<int,int>::list_cit it = listItems.begin(); it != listItems.end(); ++it) {
const CacheItem<int,int>& item = *it;
std::cout << item.key << " : " << item.value << std::endl;
}
}
bool Compare(const CacheMultiMap<int,int>& map1, const CacheMultiMap<int,int>& map2 )
{
if(map1.GetMaxSize() != map2.GetMaxSize()) {
std::cout << "Compare returning false: max size mismatch" << std::endl;
return false;
}
if(map1.GetSize() != map2.GetSize()) {
std::cout << "Compare returning false: size mismatch" << std::endl;
return false;
}
const CacheMultiMap<int,int>::list_t& items1 = map1.GetItemList();
const CacheMultiMap<int,int>::list_t& items2 = map2.GetItemList();
CacheMultiMap<int,int>::list_cit it2 = items2.begin();
for(CacheMultiMap<int,int>::list_cit it1 = items1.begin(); it1 != items1.end(); ++it1) {
const CacheItem<int,int>& item1 = *it1;
const CacheItem<int,int>& item2 = *it2;
if(item1.key != item2.key) {
return false;
}
if(item1.value != item2.value) {
return false;
}
++it2;
}
return true;
}
bool CheckExpected(const CacheMultiMap<int,int>& map, int* expected, CacheMultiMap<int,int>::size_type nSize)
{
if(map.GetSize() != nSize) {
return false;
}
for(CacheMultiMap<int,int>::size_type i = 0; i < nSize; ++i) {
int nVal = 0;
int eVal = expected[i];
if(!map.Get(eVal, nVal)) {
return false;
}
if(nVal != eVal) {
return false;
}
}
return true;
}
BOOST_AUTO_TEST_CASE(cachemultimap_test)
{
// create a CacheMultiMap limited to 10 items
CacheMultiMap<int,int> mapTest1(10);
// check that the max size is 10
BOOST_CHECK(mapTest1.GetMaxSize() == 10);
// check that the size is 0
BOOST_CHECK(mapTest1.GetSize() == 0);
// insert (-1, -1)
mapTest1.Insert(-1, -1);
// make sure that the size is updated
BOOST_CHECK(mapTest1.GetSize() == 1);
// make sure the map contains the key
BOOST_CHECK(mapTest1.HasKey(-1) == true);
// add 10 items
for(int i = 0; i < 10; ++i) {
mapTest1.Insert(i, i);
}
// check that the size is 10
BOOST_CHECK(mapTest1.GetSize() == 10);
// check that the map contains the expected items
for(int i = 0; i < 10; ++i) {
int nVal = 0;
BOOST_CHECK(mapTest1.Get(i, nVal) == true);
BOOST_CHECK(nVal == i);
}
// check that the map no longer contains the first item
BOOST_CHECK(mapTest1.HasKey(-1) == false);
// erase an item
mapTest1.Erase(5);
// check the size
BOOST_CHECK(mapTest1.GetSize() == 9);
// check that the map no longer contains the item
BOOST_CHECK(mapTest1.HasKey(5) == false);
// check that the map contains the expected items
int expected[] = { 0, 1, 2, 3, 4, 6, 7, 8, 9 };
BOOST_CHECK(CheckExpected(mapTest1, expected, 9 ) == true);
// add multiple items for the same key
mapTest1.Insert(5, 2);
mapTest1.Insert(5, 1);
mapTest1.Insert(5, 4);
// check the size
BOOST_CHECK(mapTest1.GetSize() == 10);
// check that 2 keys have been removed
BOOST_CHECK(mapTest1.HasKey(0) == false);
BOOST_CHECK(mapTest1.HasKey(1) == false);
BOOST_CHECK(mapTest1.HasKey(2) == true);
// check multiple values
std::vector<int> vecVals;
BOOST_CHECK(mapTest1.GetAll(5, vecVals) == true);
BOOST_CHECK(vecVals.size() == 3);
BOOST_CHECK(vecVals[0] == 1);
BOOST_CHECK(vecVals[1] == 2);
BOOST_CHECK(vecVals[2] == 4);
// std::cout << "mapTest1 dump:" << std::endl;
// DumpMap(mapTest1);
// test serialization
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << mapTest1;
CacheMultiMap<int,int> mapTest2;
ss >> mapTest2;
// std::cout << "mapTest2 dump:" << std::endl;
// DumpMap(mapTest2);
// check multiple values
std::vector<int> vecVals2;
BOOST_CHECK(mapTest2.GetAll(5, vecVals2) == true);
BOOST_CHECK(vecVals2.size() == 3);
BOOST_CHECK(vecVals2[0] == 1);
BOOST_CHECK(vecVals2[1] == 2);
BOOST_CHECK(vecVals2[2] == 4);
BOOST_CHECK(Compare(mapTest1, mapTest2));
// test copy constructor
CacheMultiMap<int,int> mapTest3(mapTest1);
BOOST_CHECK(Compare(mapTest1, mapTest3));
// test assignment operator
CacheMultiMap<int,int> mapTest4;
mapTest4 = mapTest1;
BOOST_CHECK(Compare(mapTest1, mapTest4));
}
BOOST_AUTO_TEST_SUITE_END()