dash/src/evo/deterministicmns.cpp
UdjinM6 88498ba139
Apply suggestions from code review
Co-Authored-By: codablock <ablock84@gmail.com>
2018-11-15 15:24:24 +01:00

753 lines
27 KiB
C++

// 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.
#include "deterministicmns.h"
#include "specialtx.h"
#include "base58.h"
#include "chainparams.h"
#include "core_io.h"
#include "script/standard.h"
#include "spork.h"
#include "validation.h"
#include "validationinterface.h"
#include <univalue.h>
static const std::string DB_LIST_SNAPSHOT = "dmn_S";
static const std::string DB_LIST_DIFF = "dmn_D";
CDeterministicMNManager* deterministicMNManager;
std::string CDeterministicMNState::ToString() const
{
CTxDestination dest;
std::string payoutAddress = "unknown";
std::string operatorRewardAddress = "none";
if (ExtractDestination(scriptPayout, dest)) {
payoutAddress = CBitcoinAddress(dest).ToString();
}
if (ExtractDestination(scriptOperatorPayout, dest)) {
operatorRewardAddress = CBitcoinAddress(dest).ToString();
}
return strprintf("CDeterministicMNState(nRegisteredHeight=%d, nLastPaidHeight=%d, nPoSePenalty=%d, nPoSeRevivedHeight=%d, nPoSeBanHeight=%d, nRevocationReason=%d, "
"keyIDOwner=%s, pubKeyOperator=%s, keyIDVoting=%s, addr=%s, payoutAddress=%s, operatorRewardAddress=%s)",
nRegisteredHeight, nLastPaidHeight, nPoSePenalty, nPoSeRevivedHeight, nPoSeBanHeight, nRevocationReason,
keyIDOwner.ToString(), pubKeyOperator.ToString(), keyIDVoting.ToString(), addr.ToStringIPPort(false), payoutAddress, operatorRewardAddress);
}
void CDeterministicMNState::ToJson(UniValue& obj) const
{
obj.clear();
obj.setObject();
obj.push_back(Pair("registeredHeight", nRegisteredHeight));
obj.push_back(Pair("lastPaidHeight", nLastPaidHeight));
obj.push_back(Pair("PoSePenalty", nPoSePenalty));
obj.push_back(Pair("PoSeRevivedHeight", nPoSeRevivedHeight));
obj.push_back(Pair("PoSeBanHeight", nPoSeBanHeight));
obj.push_back(Pair("revocationReason", nRevocationReason));
obj.push_back(Pair("keyIDOwner", keyIDOwner.ToString()));
obj.push_back(Pair("pubKeyOperator", pubKeyOperator.ToString()));
obj.push_back(Pair("keyIDVoting", keyIDVoting.ToString()));
obj.push_back(Pair("addr", addr.ToStringIPPort(false)));
CTxDestination dest;
if (ExtractDestination(scriptPayout, dest)) {
CBitcoinAddress bitcoinAddress(dest);
obj.push_back(Pair("payoutAddress", bitcoinAddress.ToString()));
}
if (ExtractDestination(scriptOperatorPayout, dest)) {
CBitcoinAddress bitcoinAddress(dest);
obj.push_back(Pair("operatorRewardAddress", bitcoinAddress.ToString()));
}
}
std::string CDeterministicMN::ToString() const
{
return strprintf("CDeterministicMN(proTxHash=%s, collateralOutpoint=%s, nOperatorReward=%f, state=%s", proTxHash.ToString(), collateralOutpoint.ToStringShort(), (double)nOperatorReward / 100, pdmnState->ToString());
}
void CDeterministicMN::ToJson(UniValue& obj) const
{
obj.clear();
obj.setObject();
UniValue stateObj;
pdmnState->ToJson(stateObj);
obj.push_back(Pair("proTxHash", proTxHash.ToString()));
obj.push_back(Pair("collateralHash", collateralOutpoint.hash.ToString()));
obj.push_back(Pair("collateralIndex", (int)collateralOutpoint.n));
obj.push_back(Pair("operatorReward", (double)nOperatorReward / 100));
obj.push_back(Pair("state", stateObj));
}
bool CDeterministicMNList::IsMNValid(const uint256& proTxHash) const
{
auto p = mnMap.find(proTxHash);
if (p == nullptr) {
return false;
}
return IsMNValid(*p);
}
bool CDeterministicMNList::IsMNPoSeBanned(const uint256& proTxHash) const
{
auto p = mnMap.find(proTxHash);
if (p == nullptr) {
return false;
}
return IsMNPoSeBanned(*p);
}
bool CDeterministicMNList::IsMNValid(const CDeterministicMNCPtr& dmn) const
{
return !IsMNPoSeBanned(dmn);
}
bool CDeterministicMNList::IsMNPoSeBanned(const CDeterministicMNCPtr& dmn) const
{
assert(dmn);
const CDeterministicMNState& state = *dmn->pdmnState;
return state.nPoSeBanHeight != -1;
}
CDeterministicMNCPtr CDeterministicMNList::GetMN(const uint256& proTxHash) const
{
auto p = mnMap.find(proTxHash);
if (p == nullptr) {
return nullptr;
}
return *p;
}
CDeterministicMNCPtr CDeterministicMNList::GetValidMN(const uint256& proTxHash) const
{
auto dmn = GetMN(proTxHash);
if (dmn && !IsMNValid(dmn)) {
return nullptr;
}
return dmn;
}
CDeterministicMNCPtr CDeterministicMNList::GetMNByOperatorKey(const CBLSPublicKey& pubKey)
{
for (const auto& p : mnMap) {
if (p.second->pdmnState->pubKeyOperator == pubKey) {
return p.second;
}
}
return nullptr;
}
CDeterministicMNCPtr CDeterministicMNList::GetMNByCollateral(const COutPoint& collateralOutpoint) const
{
return GetUniquePropertyMN(collateralOutpoint);
}
static int CompareByLastPaid_GetHeight(const CDeterministicMN& dmn)
{
int height = dmn.pdmnState->nLastPaidHeight;
if (dmn.pdmnState->nPoSeRevivedHeight != -1 && dmn.pdmnState->nPoSeRevivedHeight > height) {
height = dmn.pdmnState->nPoSeRevivedHeight;
} else if (height == 0) {
height = dmn.pdmnState->nRegisteredHeight;
}
return height;
}
static bool CompareByLastPaid(const CDeterministicMN& _a, const CDeterministicMN& _b)
{
int ah = CompareByLastPaid_GetHeight(_a);
int bh = CompareByLastPaid_GetHeight(_b);
if (ah == bh) {
return _a.proTxHash < _b.proTxHash;
} else {
return ah < bh;
}
}
static bool CompareByLastPaid(const CDeterministicMNCPtr& _a, const CDeterministicMNCPtr& _b)
{
return CompareByLastPaid(*_a, *_b);
}
CDeterministicMNCPtr CDeterministicMNList::GetMNPayee() const
{
if (mnMap.size() == 0) {
return nullptr;
}
CDeterministicMNCPtr best;
ForEachMN(true, [&](const CDeterministicMNCPtr& dmn) {
if (!best || CompareByLastPaid(dmn, best)) {
best = dmn;
}
});
return best;
}
std::vector<CDeterministicMNCPtr> CDeterministicMNList::GetProjectedMNPayees(int nCount) const
{
std::vector<CDeterministicMNCPtr> result;
result.reserve(nCount);
CDeterministicMNList tmpMNList = *this;
for (int h = nHeight; h < nHeight + nCount; h++) {
tmpMNList.SetHeight(h);
CDeterministicMNCPtr payee = tmpMNList.GetMNPayee();
// push the original MN object instead of the one from the temporary list
result.push_back(GetMN(payee->proTxHash));
CDeterministicMNStatePtr newState = std::make_shared<CDeterministicMNState>(*payee->pdmnState);
newState->nLastPaidHeight = h;
tmpMNList.UpdateMN(payee->proTxHash, newState);
}
return result;
}
std::vector<CDeterministicMNCPtr> CDeterministicMNList::CalculateQuorum(size_t maxSize, const uint256& modifier) const
{
auto scores = CalculateScores(modifier);
// sort is descending order
std::sort(scores.rbegin(), scores.rend(), [](const std::pair<arith_uint256, CDeterministicMNCPtr>& a, std::pair<arith_uint256, CDeterministicMNCPtr>& b) {
if (a.first == b.first) {
// this should actually never happen, but we should stay compatible with how the non deterministic MNs did the sorting
return a.second->collateralOutpoint < b.second->collateralOutpoint;
}
return a.first < b.first;
});
// take top maxSize entries and return it
std::vector<CDeterministicMNCPtr> result;
result.resize(std::min(maxSize, scores.size()));
for (size_t i = 0; i < result.size(); i++) {
result[i] = std::move(scores[i].second);
}
return result;
}
std::vector<std::pair<arith_uint256, CDeterministicMNCPtr>> CDeterministicMNList::CalculateScores(const uint256& modifier) const
{
std::vector<std::pair<arith_uint256, CDeterministicMNCPtr>> scores;
scores.reserve(GetAllMNsCount());
ForEachMN(true, [&](const CDeterministicMNCPtr& dmn) {
if (dmn->pdmnState->confirmedHash.IsNull()) {
// we only take confirmed MNs into account to avoid hash grinding on the ProRegTxHash to sneak MNs into a
// future quorums
return;
}
// calculate sha256(sha256(proTxHash, confirmedHash), modifier) per MN
// Please note that this is not a double-sha256 but a single-sha256
// The first part is already precalculated (confirmedHashWithProRegTxHash)
// TODO When https://github.com/bitcoin/bitcoin/pull/13191 gets backported, implement something that is similar but for single-sha256
uint256 h;
CSHA256 sha256;
sha256.Write(dmn->pdmnState->confirmedHashWithProRegTxHash.begin(), dmn->pdmnState->confirmedHashWithProRegTxHash.size());
sha256.Write(modifier.begin(), modifier.size());
sha256.Finalize(h.begin());
scores.emplace_back(UintToArith256(h), dmn);
});
return scores;
}
CDeterministicMNListDiff CDeterministicMNList::BuildDiff(const CDeterministicMNList& to) const
{
CDeterministicMNListDiff diffRet;
diffRet.prevBlockHash = blockHash;
diffRet.blockHash = to.blockHash;
diffRet.nHeight = to.nHeight;
to.ForEachMN(false, [&](const CDeterministicMNCPtr& toPtr) {
auto fromPtr = GetMN(toPtr->proTxHash);
if (fromPtr == nullptr) {
diffRet.addedMNs.emplace(toPtr->proTxHash, toPtr);
} else if (*toPtr->pdmnState != *fromPtr->pdmnState) {
diffRet.updatedMNs.emplace(toPtr->proTxHash, toPtr->pdmnState);
}
});
ForEachMN(false, [&](const CDeterministicMNCPtr& fromPtr) {
auto toPtr = to.GetMN(fromPtr->proTxHash);
if (toPtr == nullptr) {
diffRet.removedMns.insert(fromPtr->proTxHash);
}
});
return diffRet;
}
CSimplifiedMNListDiff CDeterministicMNList::BuildSimplifiedDiff(const CDeterministicMNList& to) const
{
CSimplifiedMNListDiff diffRet;
diffRet.baseBlockHash = blockHash;
diffRet.blockHash = to.blockHash;
to.ForEachMN(false, [&](const CDeterministicMNCPtr& toPtr) {
auto fromPtr = GetMN(toPtr->proTxHash);
if (fromPtr == nullptr) {
diffRet.mnList.emplace_back(*toPtr);
} else {
CSimplifiedMNListEntry sme1(*toPtr);
CSimplifiedMNListEntry sme2(*fromPtr);
if (sme1 != sme2) {
diffRet.mnList.emplace_back(*toPtr);
}
}
});
ForEachMN(false, [&](const CDeterministicMNCPtr& fromPtr) {
auto toPtr = to.GetMN(fromPtr->proTxHash);
if (toPtr == nullptr) {
diffRet.deletedMNs.emplace_back(fromPtr->proTxHash);
}
});
return diffRet;
}
CDeterministicMNList CDeterministicMNList::ApplyDiff(const CDeterministicMNListDiff& diff) const
{
assert(diff.prevBlockHash == blockHash && diff.nHeight == nHeight + 1);
CDeterministicMNList result = *this;
result.blockHash = diff.blockHash;
result.nHeight = diff.nHeight;
for (const auto& hash : diff.removedMns) {
result.RemoveMN(hash);
}
for (const auto& p : diff.addedMNs) {
result.AddMN(p.second);
}
for (const auto& p : diff.updatedMNs) {
result.UpdateMN(p.first, p.second);
}
return result;
}
void CDeterministicMNList::AddMN(const CDeterministicMNCPtr& dmn)
{
assert(!mnMap.find(dmn->proTxHash));
mnMap = mnMap.set(dmn->proTxHash, dmn);
AddUniqueProperty(dmn, dmn->collateralOutpoint);
AddUniqueProperty(dmn, dmn->pdmnState->addr);
AddUniqueProperty(dmn, dmn->pdmnState->keyIDOwner);
AddUniqueProperty(dmn, dmn->pdmnState->pubKeyOperator);
}
void CDeterministicMNList::UpdateMN(const uint256& proTxHash, const CDeterministicMNStateCPtr& pdmnState)
{
auto oldDmn = mnMap.find(proTxHash);
assert(oldDmn != nullptr);
auto dmn = std::make_shared<CDeterministicMN>(**oldDmn);
auto oldState = dmn->pdmnState;
dmn->pdmnState = pdmnState;
mnMap = mnMap.set(proTxHash, dmn);
UpdateUniqueProperty(dmn, oldState->addr, pdmnState->addr);
UpdateUniqueProperty(dmn, oldState->keyIDOwner, pdmnState->keyIDOwner);
UpdateUniqueProperty(dmn, oldState->pubKeyOperator, pdmnState->pubKeyOperator);
}
void CDeterministicMNList::RemoveMN(const uint256& proTxHash)
{
auto dmn = GetMN(proTxHash);
assert(dmn != nullptr);
DeleteUniqueProperty(dmn, dmn->collateralOutpoint);
DeleteUniqueProperty(dmn, dmn->pdmnState->addr);
DeleteUniqueProperty(dmn, dmn->pdmnState->keyIDOwner);
DeleteUniqueProperty(dmn, dmn->pdmnState->pubKeyOperator);
mnMap = mnMap.erase(proTxHash);
}
CDeterministicMNManager::CDeterministicMNManager(CEvoDB& _evoDb) :
evoDb(_evoDb)
{
}
bool CDeterministicMNManager::ProcessBlock(const CBlock& block, const CBlockIndex* pindexPrev, CValidationState& _state)
{
LOCK(cs);
int nHeight = pindexPrev->nHeight + 1;
CDeterministicMNList newList;
if (!BuildNewListFromBlock(block, pindexPrev, _state, newList)) {
return false;
}
if (newList.GetHeight() == -1) {
newList.SetHeight(nHeight);
}
newList.SetBlockHash(block.GetHash());
CDeterministicMNList oldList = GetListForBlock(pindexPrev->GetBlockHash());
CDeterministicMNListDiff diff = oldList.BuildDiff(newList);
evoDb.Write(std::make_pair(DB_LIST_DIFF, diff.blockHash), diff);
if ((nHeight % SNAPSHOT_LIST_PERIOD) == 0) {
evoDb.Write(std::make_pair(DB_LIST_SNAPSHOT, diff.blockHash), newList);
LogPrintf("CDeterministicMNManager::%s -- Wrote snapshot. nHeight=%d, mapCurMNs.allMNsCount=%d\n",
__func__, nHeight, newList.GetAllMNsCount());
}
if (nHeight == GetSpork15Value()) {
LogPrintf("CDeterministicMNManager::%s -- spork15 is active now. nHeight=%d\n", __func__, nHeight);
}
CleanupCache(nHeight);
return true;
}
bool CDeterministicMNManager::UndoBlock(const CBlock& block, const CBlockIndex* pindex)
{
LOCK(cs);
int nHeight = pindex->nHeight;
uint256 blockHash = block.GetHash();
evoDb.Erase(std::make_pair(DB_LIST_DIFF, blockHash));
evoDb.Erase(std::make_pair(DB_LIST_SNAPSHOT, blockHash));
mnListsCache.erase(blockHash);
if (nHeight == GetSpork15Value()) {
LogPrintf("CDeterministicMNManager::%s -- spork15 is not active anymore. nHeight=%d\n", __func__, nHeight);
}
return true;
}
void CDeterministicMNManager::UpdatedBlockTip(const CBlockIndex* pindex)
{
LOCK(cs);
tipHeight = pindex->nHeight;
tipBlockHash = pindex->GetBlockHash();
}
bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const CBlockIndex* pindexPrev, CValidationState& _state, CDeterministicMNList& mnListRet)
{
AssertLockHeld(cs);
int nHeight = pindexPrev->nHeight + 1;
CDeterministicMNList oldList = GetListForBlock(pindexPrev->GetBlockHash());
CDeterministicMNList newList = oldList;
newList.SetBlockHash(uint256()); // we can't know the final block hash, so better not return a (invalid) block hash
newList.SetHeight(nHeight);
auto payee = oldList.GetMNPayee();
// we iterate the oldList here and update the newList
// this is only valid as long these have not diverged at this point, which is the case as long as we don't add
// code above this loop that modifies newList
oldList.ForEachMN(false, [&](const CDeterministicMNCPtr& dmn) {
if (!dmn->pdmnState->confirmedHash.IsNull()) {
// already confirmed
return;
}
// this works on the previous block, so confirmation will happen one block after nMasternodeMinimumConfirmations
// has been reached, but the block hash will then point to the block at nMasternodeMinimumConfirmations
int nConfirmations = pindexPrev->nHeight - dmn->pdmnState->nRegisteredHeight;
if (nConfirmations >= Params().GetConsensus().nMasternodeMinimumConfirmations) {
CDeterministicMNState newState = *dmn->pdmnState;
newState.UpdateConfirmedHash(dmn->proTxHash, pindexPrev->GetBlockHash());
newList.UpdateMN(dmn->proTxHash, std::make_shared<CDeterministicMNState>(newState));
}
});
// we skip the coinbase
for (int i = 1; i < (int)block.vtx.size(); i++) {
const CTransaction& tx = *block.vtx[i];
// check if any existing MN collateral is spent by this transaction
for (const auto& in : tx.vin) {
auto dmn = newList.GetMNByCollateral(in.prevout);
if (dmn && dmn->collateralOutpoint == in.prevout) {
newList.RemoveMN(dmn->proTxHash);
LogPrintf("CDeterministicMNManager::%s -- MN %s removed from list because collateral was spent. collateralOutpoint=%s, nHeight=%d, mapCurMNs.allMNsCount=%d\n",
__func__, dmn->proTxHash.ToString(), dmn->collateralOutpoint.ToStringShort(), nHeight, newList.GetAllMNsCount());
}
}
if (tx.nType == TRANSACTION_PROVIDER_REGISTER) {
CProRegTx proTx;
if (!GetTxPayload(tx, proTx)) {
assert(false); // this should have been handled already
}
auto dmn = std::make_shared<CDeterministicMN>();
dmn->proTxHash = tx.GetHash();
// collateralOutpoint is either pointing to an external collateral or to the ProRegTx itself
if (proTx.collateralOutpoint.hash.IsNull()) {
dmn->collateralOutpoint = COutPoint(tx.GetHash(), proTx.collateralOutpoint.n);
} else {
dmn->collateralOutpoint = proTx.collateralOutpoint;
}
Coin coin;
if (!proTx.collateralOutpoint.hash.IsNull() && (!GetUTXOCoin(dmn->collateralOutpoint, coin) || coin.out.nValue != 1000 * COIN)) {
// should actually never get to this point as CheckProRegTx should have handled this case.
// We do this additional check nevertheless to be 100% sure
return _state.DoS(100, false, REJECT_INVALID, "bad-protx-collateral");
}
auto replacedDmn = newList.GetMNByCollateral(dmn->collateralOutpoint);
if (replacedDmn != nullptr) {
// This might only happen with a ProRegTx that refers an external collateral
// In that case the new ProRegTx will replace the old one. This means the old one is removed
// and the new one is added like a completely fresh one, which is also at the bottom of the payment list
newList.RemoveMN(replacedDmn->proTxHash);
LogPrintf("CDeterministicMNManager::%s -- MN %s removed from list because collateral was used for a new ProRegTx. collateralOutpoint=%s, nHeight=%d, mapCurMNs.allMNsCount=%d\n",
__func__, replacedDmn->proTxHash.ToString(), dmn->collateralOutpoint.ToStringShort(), nHeight, newList.GetAllMNsCount());
}
if (newList.HasUniqueProperty(proTx.addr)) {
return _state.DoS(100, false, REJECT_CONFLICT, "bad-protx-dup-addr");
}
if (newList.HasUniqueProperty(proTx.keyIDOwner) || newList.HasUniqueProperty(proTx.pubKeyOperator)) {
return _state.DoS(100, false, REJECT_CONFLICT, "bad-protx-dup-key");
}
dmn->nOperatorReward = proTx.nOperatorReward;
dmn->pdmnState = std::make_shared<CDeterministicMNState>(proTx);
CDeterministicMNState dmnState = *dmn->pdmnState;
dmnState.nRegisteredHeight = nHeight;
if (proTx.addr == CService()) {
// start in banned pdmnState as we need to wait for a ProUpServTx
dmnState.nPoSeBanHeight = nHeight;
}
dmn->pdmnState = std::make_shared<CDeterministicMNState>(dmnState);
newList.AddMN(dmn);
LogPrintf("CDeterministicMNManager::%s -- MN %s added at height %d: %s\n",
__func__, tx.GetHash().ToString(), nHeight, proTx.ToString());
} else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_SERVICE) {
CProUpServTx proTx;
if (!GetTxPayload(tx, proTx)) {
assert(false); // this should have been handled already
}
if (newList.HasUniqueProperty(proTx.addr) && newList.GetUniquePropertyMN(proTx.addr)->proTxHash != proTx.proTxHash) {
return _state.DoS(100, false, REJECT_CONFLICT, "bad-protx-dup-addr");
}
CDeterministicMNCPtr dmn = newList.GetMN(proTx.proTxHash);
if (!dmn) {
return _state.DoS(100, false, REJECT_INVALID, "bad-protx-hash");
}
auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
newState->addr = proTx.addr;
newState->scriptOperatorPayout = proTx.scriptOperatorPayout;
if (newState->nPoSeBanHeight != -1) {
// only revive when all keys are set
if (newState->pubKeyOperator.IsValid() && !newState->keyIDVoting.IsNull() && !newState->keyIDOwner.IsNull()) {
newState->nPoSeBanHeight = -1;
newState->nPoSeRevivedHeight = nHeight;
LogPrintf("CDeterministicMNManager::%s -- MN %s revived at height %d\n",
__func__, proTx.proTxHash.ToString(), nHeight);
}
}
newList.UpdateMN(proTx.proTxHash, newState);
LogPrintf("CDeterministicMNManager::%s -- MN %s updated at height %d: %s\n",
__func__, proTx.proTxHash.ToString(), nHeight, proTx.ToString());
} else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REGISTRAR) {
CProUpRegTx proTx;
if (!GetTxPayload(tx, proTx)) {
assert(false); // this should have been handled already
}
CDeterministicMNCPtr dmn = newList.GetMN(proTx.proTxHash);
if (!dmn) {
return _state.DoS(100, false, REJECT_INVALID, "bad-protx-hash");
}
auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
if (newState->pubKeyOperator != proTx.pubKeyOperator) {
// reset all operator related fields and put MN into PoSe-banned state in case the operator key changes
newState->ResetOperatorFields();
newState->BanIfNotBanned(nHeight);
}
newState->pubKeyOperator = proTx.pubKeyOperator;
newState->keyIDVoting = proTx.keyIDVoting;
newState->scriptPayout = proTx.scriptPayout;
newList.UpdateMN(proTx.proTxHash, newState);
LogPrintf("CDeterministicMNManager::%s -- MN %s updated at height %d: %s\n",
__func__, proTx.proTxHash.ToString(), nHeight, proTx.ToString());
} else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REVOKE) {
CProUpRevTx proTx;
if (!GetTxPayload(tx, proTx)) {
assert(false); // this should have been handled already
}
CDeterministicMNCPtr dmn = newList.GetMN(proTx.proTxHash);
if (!dmn) {
return _state.DoS(100, false, REJECT_INVALID, "bad-protx-hash");
}
auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
newState->ResetOperatorFields();
newState->BanIfNotBanned(nHeight);
newState->nRevocationReason = proTx.nReason;
newList.UpdateMN(proTx.proTxHash, newState);
LogPrintf("CDeterministicMNManager::%s -- MN %s revoked operator key at height %d: %s\n",
__func__, proTx.proTxHash.ToString(), nHeight, proTx.ToString());
}
}
// The payee for the current block was determined by the previous block's list but it might have disappeared in the
// current block. We still pay that MN one last time however.
if (payee && newList.HasMN(payee->proTxHash)) {
auto newState = std::make_shared<CDeterministicMNState>(*newList.GetMN(payee->proTxHash)->pdmnState);
newState->nLastPaidHeight = nHeight;
newList.UpdateMN(payee->proTxHash, newState);
}
mnListRet = std::move(newList);
return true;
}
CDeterministicMNList CDeterministicMNManager::GetListForBlock(const uint256& blockHash)
{
LOCK(cs);
auto it = mnListsCache.find(blockHash);
if (it != mnListsCache.end()) {
return it->second;
}
uint256 blockHashTmp = blockHash;
CDeterministicMNList snapshot;
std::list<CDeterministicMNListDiff> listDiff;
while (true) {
// try using cache before reading from disk
it = mnListsCache.find(blockHashTmp);
if (it != mnListsCache.end()) {
snapshot = it->second;
break;
}
if (evoDb.Read(std::make_pair(DB_LIST_SNAPSHOT, blockHashTmp), snapshot)) {
break;
}
CDeterministicMNListDiff diff;
if (!evoDb.Read(std::make_pair(DB_LIST_DIFF, blockHashTmp), diff)) {
snapshot = CDeterministicMNList(blockHashTmp, -1);
break;
}
listDiff.emplace_front(diff);
blockHashTmp = diff.prevBlockHash;
}
for (const auto& diff : listDiff) {
if (diff.HasChanges()) {
snapshot = snapshot.ApplyDiff(diff);
} else {
snapshot.SetBlockHash(diff.blockHash);
snapshot.SetHeight(diff.nHeight);
}
}
mnListsCache.emplace(blockHash, snapshot);
return snapshot;
}
CDeterministicMNList CDeterministicMNManager::GetListAtChainTip()
{
LOCK(cs);
return GetListForBlock(tipBlockHash);
}
bool CDeterministicMNManager::HasValidMNCollateralAtChainTip(const COutPoint& outpoint)
{
auto mnList = GetListAtChainTip();
auto dmn = mnList.GetMNByCollateral(outpoint);
return dmn && mnList.IsMNValid(dmn);
}
bool CDeterministicMNManager::HasMNCollateralAtChainTip(const COutPoint& outpoint)
{
auto mnList = GetListAtChainTip();
auto dmn = mnList.GetMNByCollateral(outpoint);
return dmn != nullptr;
}
int64_t CDeterministicMNManager::GetSpork15Value()
{
return sporkManager.GetSporkValue(SPORK_15_DETERMINISTIC_MNS_ENABLED);
}
bool CDeterministicMNManager::IsProTxWithCollateral(const CTransactionRef& tx, uint32_t n)
{
if (tx->nVersion != 3 || tx->nType != TRANSACTION_PROVIDER_REGISTER) {
return false;
}
CProRegTx proTx;
if (!GetTxPayload(*tx, proTx)) {
return false;
}
if (!proTx.collateralOutpoint.hash.IsNull()) {
return false;
}
if (proTx.collateralOutpoint.n >= tx->vout.size() || proTx.collateralOutpoint.n != n) {
return false;
}
if (tx->vout[n].nValue != 1000 * COIN) {
return false;
}
return true;
}
bool CDeterministicMNManager::IsDeterministicMNsSporkActive(int nHeight)
{
LOCK(cs);
if (nHeight == -1) {
nHeight = tipHeight;
}
int64_t spork15Value = GetSpork15Value();
return nHeight >= spork15Value;
}
void CDeterministicMNManager::CleanupCache(int nHeight)
{
AssertLockHeld(cs);
std::vector<uint256> toDelete;
for (const auto& p : mnListsCache) {
if (p.second.GetHeight() + LISTS_CACHE_SIZE < nHeight) {
toDelete.emplace_back(p.first);
}
}
for (const auto& h : toDelete) {
mnListsCache.erase(h);
}
}