2018-02-14 14:43:03 +01:00
|
|
|
// Copyright (c) 2018 The Dash Core developers
|
|
|
|
// Distributed under the MIT software license, see the accompanying
|
|
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
|
|
|
|
#ifndef DASH_DETERMINISTICMNS_H
|
|
|
|
#define DASH_DETERMINISTICMNS_H
|
|
|
|
|
|
|
|
#include "evodb.h"
|
|
|
|
#include "providertx.h"
|
|
|
|
#include "dbwrapper.h"
|
|
|
|
#include "sync.h"
|
2018-10-21 21:45:16 +02:00
|
|
|
#include "bls/bls.h"
|
2018-02-14 14:43:03 +01:00
|
|
|
|
|
|
|
#include "immer/map.hpp"
|
|
|
|
#include "immer/map_transient.hpp"
|
|
|
|
|
|
|
|
#include <map>
|
|
|
|
|
|
|
|
class CBlock;
|
|
|
|
class CBlockIndex;
|
|
|
|
class CValidationState;
|
|
|
|
|
|
|
|
class CDeterministicMNState
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
int nRegisteredHeight{-1};
|
|
|
|
int nLastPaidHeight{0};
|
2018-08-31 11:33:37 +02:00
|
|
|
int nPoSePenalty{0};
|
2018-02-14 14:43:03 +01:00
|
|
|
int nPoSeRevivedHeight{-1};
|
|
|
|
int nPoSeBanHeight{-1};
|
2018-03-19 12:29:59 +01:00
|
|
|
uint16_t nRevocationReason{CProUpRevTx::REASON_NOT_SPECIFIED};
|
2018-02-14 14:43:03 +01:00
|
|
|
|
|
|
|
CKeyID keyIDOwner;
|
2018-10-21 21:45:16 +02:00
|
|
|
CBLSPublicKey pubKeyOperator;
|
2018-02-14 14:43:03 +01:00
|
|
|
CKeyID keyIDVoting;
|
|
|
|
CService addr;
|
|
|
|
int32_t nProtocolVersion;
|
|
|
|
CScript scriptPayout;
|
|
|
|
CScript scriptOperatorPayout;
|
|
|
|
|
|
|
|
public:
|
|
|
|
CDeterministicMNState() {}
|
|
|
|
CDeterministicMNState(const CProRegTx& proTx)
|
|
|
|
{
|
|
|
|
keyIDOwner = proTx.keyIDOwner;
|
2018-10-21 21:45:16 +02:00
|
|
|
pubKeyOperator = proTx.pubKeyOperator;
|
2018-02-14 14:43:03 +01:00
|
|
|
keyIDVoting = proTx.keyIDVoting;
|
|
|
|
addr = proTx.addr;
|
|
|
|
nProtocolVersion = proTx.nProtocolVersion;
|
|
|
|
scriptPayout = proTx.scriptPayout;
|
|
|
|
}
|
|
|
|
template<typename Stream>
|
|
|
|
CDeterministicMNState(deserialize_type, Stream& s) { s >> *this;}
|
|
|
|
|
|
|
|
ADD_SERIALIZE_METHODS;
|
|
|
|
|
|
|
|
template <typename Stream, typename Operation>
|
|
|
|
inline void SerializationOp(Stream& s, Operation ser_action)
|
|
|
|
{
|
|
|
|
READWRITE(nRegisteredHeight);
|
|
|
|
READWRITE(nLastPaidHeight);
|
2018-08-31 11:33:37 +02:00
|
|
|
READWRITE(nPoSePenalty);
|
2018-02-14 14:43:03 +01:00
|
|
|
READWRITE(nPoSeRevivedHeight);
|
|
|
|
READWRITE(nPoSeBanHeight);
|
2018-03-19 12:29:59 +01:00
|
|
|
READWRITE(nRevocationReason);
|
2018-02-14 14:43:03 +01:00
|
|
|
READWRITE(keyIDOwner);
|
2018-10-21 21:45:16 +02:00
|
|
|
READWRITE(pubKeyOperator);
|
2018-02-14 14:43:03 +01:00
|
|
|
READWRITE(keyIDVoting);
|
|
|
|
READWRITE(addr);
|
|
|
|
READWRITE(nProtocolVersion);
|
|
|
|
READWRITE(*(CScriptBase*)(&scriptPayout));
|
|
|
|
READWRITE(*(CScriptBase*)(&scriptOperatorPayout));
|
|
|
|
}
|
|
|
|
|
2018-03-19 08:44:00 +01:00
|
|
|
void ResetOperatorFields()
|
|
|
|
{
|
2018-10-21 21:45:16 +02:00
|
|
|
pubKeyOperator = CBLSPublicKey();
|
2018-03-19 08:44:00 +01:00
|
|
|
addr = CService();
|
|
|
|
nProtocolVersion = 0;
|
|
|
|
scriptOperatorPayout = CScript();
|
2018-03-19 12:29:59 +01:00
|
|
|
nRevocationReason = CProUpRevTx::REASON_NOT_SPECIFIED;
|
2018-03-19 08:44:00 +01:00
|
|
|
}
|
|
|
|
void BanIfNotBanned(int height)
|
|
|
|
{
|
|
|
|
if (nPoSeBanHeight == -1) {
|
|
|
|
nPoSeBanHeight = height;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-14 14:43:03 +01:00
|
|
|
bool operator==(const CDeterministicMNState& rhs) const
|
|
|
|
{
|
|
|
|
return nRegisteredHeight == rhs.nRegisteredHeight &&
|
|
|
|
nLastPaidHeight == rhs.nLastPaidHeight &&
|
2018-08-31 11:33:37 +02:00
|
|
|
nPoSePenalty == rhs.nPoSePenalty &&
|
2018-02-14 14:43:03 +01:00
|
|
|
nPoSeRevivedHeight == rhs.nPoSeRevivedHeight &&
|
|
|
|
nPoSeBanHeight == rhs.nPoSeBanHeight &&
|
2018-03-19 12:29:59 +01:00
|
|
|
nRevocationReason == rhs.nRevocationReason &&
|
2018-02-14 14:43:03 +01:00
|
|
|
keyIDOwner == rhs.keyIDOwner &&
|
2018-10-21 21:45:16 +02:00
|
|
|
pubKeyOperator == rhs.pubKeyOperator &&
|
2018-02-14 14:43:03 +01:00
|
|
|
keyIDVoting == rhs.keyIDVoting &&
|
|
|
|
addr == rhs.addr &&
|
|
|
|
nProtocolVersion == rhs.nProtocolVersion &&
|
|
|
|
scriptPayout == rhs.scriptPayout &&
|
|
|
|
scriptOperatorPayout == rhs.scriptOperatorPayout;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool operator!=(const CDeterministicMNState& rhs) const
|
|
|
|
{
|
|
|
|
return !(rhs == *this);
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
std::string ToString() const;
|
|
|
|
void ToJson(UniValue& obj) const;
|
|
|
|
};
|
|
|
|
typedef std::shared_ptr<CDeterministicMNState> CDeterministicMNStatePtr;
|
|
|
|
typedef std::shared_ptr<const CDeterministicMNState> CDeterministicMNStateCPtr;
|
|
|
|
|
|
|
|
class CDeterministicMN
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
CDeterministicMN() {}
|
|
|
|
CDeterministicMN(const uint256& _proTxHash, const CProRegTx& _proTx)
|
|
|
|
{
|
|
|
|
proTxHash = _proTxHash;
|
|
|
|
nCollateralIndex = _proTx.nCollateralIndex;
|
|
|
|
nOperatorReward = _proTx.nOperatorReward;
|
|
|
|
pdmnState = std::make_shared<CDeterministicMNState>(_proTx);
|
|
|
|
}
|
|
|
|
template<typename Stream>
|
|
|
|
CDeterministicMN(deserialize_type, Stream& s) { s >> *this;}
|
|
|
|
|
|
|
|
uint256 proTxHash;
|
|
|
|
uint32_t nCollateralIndex;
|
|
|
|
uint16_t nOperatorReward;
|
|
|
|
CDeterministicMNStateCPtr pdmnState;
|
|
|
|
|
|
|
|
public:
|
|
|
|
ADD_SERIALIZE_METHODS;
|
|
|
|
|
|
|
|
template <typename Stream, typename Operation>
|
|
|
|
inline void SerializationOp(Stream& s, Operation ser_action)
|
|
|
|
{
|
|
|
|
READWRITE(proTxHash);
|
|
|
|
READWRITE(nCollateralIndex);
|
|
|
|
READWRITE(nOperatorReward);
|
|
|
|
READWRITE(pdmnState);
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
std::string ToString() const;
|
|
|
|
void ToJson(UniValue& obj) const;
|
|
|
|
};
|
|
|
|
typedef std::shared_ptr<CDeterministicMN> CDeterministicMNPtr;
|
|
|
|
typedef std::shared_ptr<const CDeterministicMN> CDeterministicMNCPtr;
|
|
|
|
|
|
|
|
class CDeterministicMNListDiff;
|
|
|
|
|
|
|
|
template<typename Stream, typename K, typename T, typename Hash, typename Equal>
|
|
|
|
void SerializeImmerMap(Stream& os, const immer::map<K, T, Hash, Equal>& m)
|
|
|
|
{
|
|
|
|
WriteCompactSize(os, m.size());
|
|
|
|
for (typename immer::map<K, T, Hash, Equal>::const_iterator mi = m.begin(); mi != m.end(); ++mi)
|
|
|
|
Serialize(os, (*mi));
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename Stream, typename K, typename T, typename Hash, typename Equal>
|
|
|
|
void UnserializeImmerMap(Stream& is, immer::map<K, T, Hash, Equal>& m)
|
|
|
|
{
|
|
|
|
m = immer::map<K, T, Hash, Equal>();
|
|
|
|
unsigned int nSize = ReadCompactSize(is);
|
|
|
|
for (unsigned int i = 0; i < nSize; i++)
|
|
|
|
{
|
|
|
|
std::pair<K, T> item;
|
|
|
|
Unserialize(is, item);
|
|
|
|
m = m.set(item.first, item.second);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class CDeterministicMNList
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
typedef immer::map<uint256, CDeterministicMNCPtr> MnMap;
|
|
|
|
typedef immer::map<uint256, std::pair<uint256, uint32_t>> MnUniquePropertyMap;
|
|
|
|
|
|
|
|
private:
|
|
|
|
uint256 blockHash;
|
|
|
|
int nHeight{-1};
|
|
|
|
MnMap mnMap;
|
|
|
|
|
|
|
|
// map of unique properties like address and keys
|
|
|
|
// we keep track of this as checking for duplicates would otherwise be painfully slow
|
|
|
|
// the entries in the map are ref counted as some properties might appear multiple times per MN (e.g. operator/owner keys)
|
|
|
|
MnUniquePropertyMap mnUniquePropertyMap;
|
|
|
|
|
|
|
|
public:
|
|
|
|
CDeterministicMNList() {}
|
|
|
|
explicit CDeterministicMNList(const uint256& _blockHash, int _height) :
|
|
|
|
blockHash(_blockHash),
|
|
|
|
nHeight(_height)
|
|
|
|
{}
|
|
|
|
|
|
|
|
ADD_SERIALIZE_METHODS;
|
|
|
|
|
|
|
|
template <typename Stream, typename Operation>
|
|
|
|
inline void SerializationOp(Stream& s, Operation ser_action)
|
|
|
|
{
|
|
|
|
READWRITE(blockHash);
|
|
|
|
READWRITE(nHeight);
|
|
|
|
if (ser_action.ForRead()) {
|
|
|
|
UnserializeImmerMap(s, mnMap);
|
|
|
|
UnserializeImmerMap(s, mnUniquePropertyMap);
|
|
|
|
} else {
|
|
|
|
SerializeImmerMap(s, mnMap);
|
|
|
|
SerializeImmerMap(s, mnUniquePropertyMap);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
2018-10-02 11:03:05 +02:00
|
|
|
size_t GetAllMNsCount() const
|
2018-02-14 14:43:03 +01:00
|
|
|
{
|
|
|
|
return mnMap.size();
|
|
|
|
}
|
|
|
|
|
2018-10-02 11:03:05 +02:00
|
|
|
template<typename Callback>
|
|
|
|
void ForEachMN(bool onlyValid, Callback&& cb) const {
|
2018-02-14 14:43:03 +01:00
|
|
|
for (const auto& p : mnMap) {
|
2018-10-02 11:03:05 +02:00
|
|
|
if (!onlyValid || IsMNValid(p.second)) {
|
|
|
|
cb(p.second);
|
2018-02-14 14:43:03 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
const uint256& GetBlockHash() const
|
|
|
|
{
|
|
|
|
return blockHash;
|
|
|
|
}
|
|
|
|
void SetBlockHash(const uint256& _blockHash)
|
|
|
|
{
|
|
|
|
blockHash = _blockHash;
|
|
|
|
}
|
|
|
|
int GetHeight() const
|
|
|
|
{
|
|
|
|
return nHeight;
|
|
|
|
}
|
|
|
|
void SetHeight(int _height)
|
|
|
|
{
|
|
|
|
nHeight = _height;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsMNValid(const uint256& proTxHash) const;
|
|
|
|
bool IsMNPoSeBanned(const uint256& proTxHash) const;
|
|
|
|
|
|
|
|
bool HasMN(const uint256& proTxHash) const {
|
|
|
|
return GetMN(proTxHash) != nullptr;
|
|
|
|
}
|
|
|
|
CDeterministicMNCPtr GetMN(const uint256& proTxHash) const;
|
|
|
|
CDeterministicMNCPtr GetValidMN(const uint256& proTxHash) const;
|
2018-10-21 21:45:16 +02:00
|
|
|
CDeterministicMNCPtr GetMNByOperatorKey(const CBLSPublicKey& pubKey);
|
2018-02-14 14:43:03 +01:00
|
|
|
CDeterministicMNCPtr GetMNPayee() const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Calculates the projected MN payees for the next *count* blocks. The result is not guaranteed to be correct
|
|
|
|
* as PoSe banning might occur later
|
|
|
|
* @param count
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
std::vector<CDeterministicMNCPtr> GetProjectedMNPayees(int nCount) const;
|
|
|
|
|
|
|
|
CDeterministicMNListDiff BuildDiff(const CDeterministicMNList& to) const;
|
|
|
|
CDeterministicMNList ApplyDiff(const CDeterministicMNListDiff& diff) const;
|
|
|
|
|
|
|
|
void AddMN(const CDeterministicMNCPtr &dmn);
|
|
|
|
void UpdateMN(const uint256 &proTxHash, const CDeterministicMNStateCPtr &pdmnState);
|
|
|
|
void RemoveMN(const uint256& proTxHash);
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
bool HasUniqueProperty(const T& v) const
|
|
|
|
{
|
|
|
|
return mnUniquePropertyMap.count(::SerializeHash(v)) != 0;
|
|
|
|
}
|
|
|
|
template<typename T>
|
|
|
|
CDeterministicMNCPtr GetUniquePropertyMN(const T& v) const
|
|
|
|
{
|
|
|
|
auto p = mnUniquePropertyMap.find(::SerializeHash(v));
|
|
|
|
if (!p) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return GetMN(p->first);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
bool IsMNValid(const CDeterministicMNCPtr& dmn) const;
|
|
|
|
bool IsMNPoSeBanned(const CDeterministicMNCPtr& dmn) const;
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
void AddUniqueProperty(const CDeterministicMNCPtr& dmn, const T& v)
|
|
|
|
{
|
|
|
|
auto hash = ::SerializeHash(v);
|
|
|
|
auto oldEntry = mnUniquePropertyMap.find(hash);
|
|
|
|
assert(!oldEntry || oldEntry->first == dmn->proTxHash);
|
|
|
|
std::pair<uint256, uint32_t> newEntry(dmn->proTxHash, 1);
|
|
|
|
if (oldEntry) {
|
|
|
|
newEntry.second = oldEntry->second + 1;
|
|
|
|
}
|
|
|
|
mnUniquePropertyMap = mnUniquePropertyMap.set(hash, newEntry);
|
|
|
|
}
|
|
|
|
template<typename T>
|
|
|
|
void DeleteUniqueProperty(const CDeterministicMNCPtr& dmn, const T& oldValue)
|
|
|
|
{
|
|
|
|
auto oldHash = ::SerializeHash(oldValue);
|
|
|
|
auto p = mnUniquePropertyMap.find(oldHash);
|
|
|
|
assert(p && p->first == dmn->proTxHash);
|
|
|
|
if (p->second == 1) {
|
|
|
|
mnUniquePropertyMap = mnUniquePropertyMap.erase(oldHash);
|
|
|
|
} else {
|
|
|
|
mnUniquePropertyMap = mnUniquePropertyMap.set(oldHash, std::make_pair(dmn->proTxHash, p->second - 1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
template<typename T>
|
|
|
|
void UpdateUniqueProperty(const CDeterministicMNCPtr& dmn, const T& oldValue, const T& newValue)
|
|
|
|
{
|
|
|
|
if (oldValue == newValue) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
DeleteUniqueProperty(dmn, oldValue);
|
|
|
|
AddUniqueProperty(dmn, newValue);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class CDeterministicMNListDiff
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
uint256 prevBlockHash;
|
|
|
|
uint256 blockHash;
|
|
|
|
int nHeight{-1};
|
|
|
|
std::map<uint256, CDeterministicMNCPtr> addedMNs;
|
|
|
|
std::map<uint256, CDeterministicMNStateCPtr> updatedMNs;
|
|
|
|
std::set<uint256> removedMns;
|
|
|
|
|
|
|
|
public:
|
|
|
|
ADD_SERIALIZE_METHODS;
|
|
|
|
|
|
|
|
template <typename Stream, typename Operation>
|
|
|
|
inline void SerializationOp(Stream& s, Operation ser_action)
|
|
|
|
{
|
|
|
|
READWRITE(prevBlockHash);
|
|
|
|
READWRITE(blockHash);
|
|
|
|
READWRITE(nHeight);
|
|
|
|
READWRITE(addedMNs);
|
|
|
|
READWRITE(updatedMNs);
|
|
|
|
READWRITE(removedMns);
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
bool HasChanges() const
|
|
|
|
{
|
|
|
|
return !addedMNs.empty() || !updatedMNs.empty() || !removedMns.empty();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class CDeterministicMNManager
|
|
|
|
{
|
|
|
|
static const int SNAPSHOT_LIST_PERIOD = 576; // once per day
|
|
|
|
static const int LISTS_CACHE_SIZE = 576;
|
|
|
|
|
|
|
|
public:
|
|
|
|
CCriticalSection cs;
|
|
|
|
|
|
|
|
private:
|
|
|
|
CEvoDB& evoDb;
|
|
|
|
|
|
|
|
std::map<uint256, CDeterministicMNList> mnListsCache;
|
|
|
|
int tipHeight{-1};
|
|
|
|
uint256 tipBlockHash;
|
|
|
|
|
|
|
|
public:
|
|
|
|
CDeterministicMNManager(CEvoDB& _evoDb);
|
|
|
|
|
|
|
|
bool ProcessBlock(const CBlock& block, const CBlockIndex* pindexPrev, CValidationState& state);
|
|
|
|
bool UndoBlock(const CBlock& block, const CBlockIndex* pindex);
|
|
|
|
|
|
|
|
void UpdatedBlockTip(const CBlockIndex *pindex);
|
|
|
|
|
|
|
|
// the returned list will not contain the correct block hash (we can't know it yet as the coinbase TX is not updated yet)
|
|
|
|
bool BuildNewListFromBlock(const CBlock& block, const CBlockIndex* pindexPrev, CValidationState& state, CDeterministicMNList& mnListRet);
|
|
|
|
|
|
|
|
CDeterministicMNList GetListForBlock(const uint256& blockHash);
|
|
|
|
CDeterministicMNList GetListAtChainTip();
|
|
|
|
|
|
|
|
CDeterministicMNCPtr GetMN(const uint256& blockHash, const uint256& proTxHash);
|
|
|
|
bool HasValidMNAtBlock(const uint256& blockHash, const uint256& proTxHash);
|
|
|
|
bool HasValidMNAtChainTip(const uint256& proTxHash);
|
|
|
|
|
2018-02-15 13:57:18 +01:00
|
|
|
bool IsDeterministicMNsSporkActive(int nHeight = -1);
|
|
|
|
|
2018-02-14 14:43:03 +01:00
|
|
|
private:
|
2018-02-15 13:57:18 +01:00
|
|
|
void UpdateSpork15Value();
|
|
|
|
int64_t GetSpork15Value();
|
2018-02-14 14:43:03 +01:00
|
|
|
void CleanupCache(int nHeight);
|
|
|
|
};
|
|
|
|
|
|
|
|
extern CDeterministicMNManager* deterministicMNManager;
|
|
|
|
|
|
|
|
#endif//DASH_DETERMINISTICMNS_H
|