void CDeterministicMNList::PoSePunish(const uint256& proTxHash, int penalty, bool debugLogs) { assert(penalty > 0); auto dmn = GetMN(proTxHash); assert(dmn); int maxPenalty = CalcMaxPoSePenalty(); auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState); newState->nPoSePenalty += penalty; newState->nPoSePenalty = std::min(maxPenalty, newState->nPoSePenalty); if (debugLogs) { LogPrintf("CDeterministicMNList::%s -- punished MN %s, penalty %d->%d (max=%d)\n", __func__, proTxHash.ToString(), dmn->pdmnState->nPoSePenalty, newState->nPoSePenalty, maxPenalty); } if (newState->nPoSePenalty >= maxPenalty && newState->nPoSeBanHeight == -1) { newState->nPoSeBanHeight = nHeight; if (debugLogs) { LogPrintf("CDeterministicMNList::%s -- banned MN %s at height %d\n", __func__, proTxHash.ToString(), nHeight); } } UpdateMN(proTxHash, newState); }
bool CheckProUpRevTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state) { if (tx.nType != TRANSACTION_PROVIDER_UPDATE_REVOKE) { return state.DoS(100, false, REJECT_INVALID, "bad-protx-type"); } CProUpRevTx ptx; if (!GetTxPayload(tx, ptx)) { return state.DoS(100, false, REJECT_INVALID, "bad-protx-payload"); } if (ptx.nVersion == 0 || ptx.nVersion > CProRegTx::CURRENT_VERSION) { return state.DoS(100, false, REJECT_INVALID, "bad-protx-version"); } // ptx.nReason < CProUpRevTx::REASON_NOT_SPECIFIED is always `false` since // ptx.nReason is unsigned and CProUpRevTx::REASON_NOT_SPECIFIED == 0 if (ptx.nReason > CProUpRevTx::REASON_LAST) { return state.DoS(100, false, REJECT_INVALID, "bad-protx-reason"); } if (pindexPrev) { auto mnList = deterministicMNManager->GetListForBlock(pindexPrev->GetBlockHash()); auto dmn = mnList.GetMN(ptx.proTxHash); if (!dmn) return state.DoS(100, false, REJECT_INVALID, "bad-protx-hash"); if (!CheckInputsHash(tx, ptx, state)) return false; if (!CheckHashSig(ptx, dmn->pdmnState->pubKeyOperator, state)) return false; } return true; }
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; }
void CActiveDeterministicMasternodeManager::UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork, bool fInitialDownload) { LOCK(cs_main); if (!fMasternodeMode) return; if (!deterministicMNManager->IsDeterministicMNsSporkActive(pindexNew->nHeight)) return; if (state == MASTERNODE_READY) { auto mnList = deterministicMNManager->GetListForBlock(pindexNew->GetBlockHash()); if (!mnList.IsMNValid(mnListEntry->proTxHash)) { // MN disappeared from MN list state = MASTERNODE_REMOVED; activeMasternodeInfo.proTxHash = uint256(); activeMasternodeInfo.outpoint.SetNull(); // MN might have reappeared in same block with a new ProTx Init(); } else if (mnList.GetMN(mnListEntry->proTxHash)->pdmnState->pubKeyOperator != mnListEntry->pdmnState->pubKeyOperator) { // MN operator key changed or revoked state = MASTERNODE_OPERATOR_KEY_CHANGED; activeMasternodeInfo.proTxHash = uint256(); activeMasternodeInfo.outpoint.SetNull(); // MN might have reappeared in same block with a new ProTx Init(); } } else { // MN might have (re)appeared with a new ProTx or we've found some peers and figured out our local address Init(); } }
CDeterministicMNCPtr CDeterministicMNList::GetValidMN(const uint256& proTxHash) const { auto dmn = GetMN(proTxHash); if (dmn && !IsMNValid(dmn)) { return nullptr; } return dmn; }
void CDeterministicMNList::PoSeDecrease(const uint256& proTxHash) { auto dmn = GetMN(proTxHash); assert(dmn); assert(dmn->pdmnState->nPoSePenalty > 0 && dmn->pdmnState->nPoSeBanHeight == -1); auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState); newState->nPoSePenalty--; UpdateMN(proTxHash, newState); }
bool CheckProUpServTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state) { if (tx.nType != TRANSACTION_PROVIDER_UPDATE_SERVICE) { return state.DoS(100, false, REJECT_INVALID, "bad-protx-type"); } CProUpServTx ptx; if (!GetTxPayload(tx, ptx)) { return state.DoS(100, false, REJECT_INVALID, "bad-protx-payload"); } if (ptx.nVersion == 0 || ptx.nVersion > CProRegTx::CURRENT_VERSION) { return state.DoS(100, false, REJECT_INVALID, "bad-protx-version"); } if (!CheckService(ptx.proTxHash, ptx, state)) { return false; } if (pindexPrev) { auto mnList = deterministicMNManager->GetListForBlock(pindexPrev->GetBlockHash()); auto mn = mnList.GetMN(ptx.proTxHash); if (!mn) { return state.DoS(100, false, REJECT_INVALID, "bad-protx-hash"); } // don't allow updating to addresses already used by other MNs if (mnList.HasUniqueProperty(ptx.addr) && mnList.GetUniquePropertyMN(ptx.addr)->proTxHash != ptx.proTxHash) { return state.DoS(10, false, REJECT_DUPLICATE, "bad-protx-dup-addr"); } if (ptx.scriptOperatorPayout != CScript()) { if (mn->nOperatorReward == 0) { // don't allow to set operator reward payee in case no operatorReward was set return state.DoS(10, false, REJECT_INVALID, "bad-protx-operator-payee"); } if (!ptx.scriptOperatorPayout.IsPayToPublicKeyHash() && !ptx.scriptOperatorPayout.IsPayToScriptHash()) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-operator-payee"); } } // we can only check the signature if pindexPrev != NULL and the MN is known if (!CheckInputsHash(tx, ptx, state)) { return false; } if (!CheckHashSig(ptx, mn->pdmnState->pubKeyOperator, state)) { return false; } } return true; }
void CDeterministicMNList::RemoveMN(const uint256& proTxHash) { auto dmn = GetMN(proTxHash); assert(dmn != nullptr); DeleteUniqueProperty(dmn, dmn->collateralOutpoint); if (dmn->pdmnState->addr != CService()) { DeleteUniqueProperty(dmn, dmn->pdmnState->addr); } DeleteUniqueProperty(dmn, dmn->pdmnState->keyIDOwner); if (dmn->pdmnState->pubKeyOperator.IsValid()) { DeleteUniqueProperty(dmn, dmn->pdmnState->pubKeyOperator); } mnMap = mnMap.erase(proTxHash); }
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; }
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; }
bool CheckProUpRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state) { if (tx.nType != TRANSACTION_PROVIDER_UPDATE_REGISTRAR) { return state.DoS(100, false, REJECT_INVALID, "bad-protx-type"); } CProUpRegTx ptx; if (!GetTxPayload(tx, ptx)) { return state.DoS(100, false, REJECT_INVALID, "bad-protx-payload"); } if (ptx.nVersion == 0 || ptx.nVersion > CProRegTx::CURRENT_VERSION) { return state.DoS(100, false, REJECT_INVALID, "bad-protx-version"); } if (ptx.nMode != 0) { return state.DoS(100, false, REJECT_INVALID, "bad-protx-mode"); } if (!ptx.pubKeyOperator.IsValid() || ptx.keyIDVoting.IsNull()) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-key-null"); } if (!ptx.scriptPayout.IsPayToPublicKeyHash() && !ptx.scriptPayout.IsPayToScriptHash()) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee"); } CTxDestination payoutDest; if (!ExtractDestination(ptx.scriptPayout, payoutDest)) { // should not happen as we checked script types before return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee-dest"); } if (pindexPrev) { auto mnList = deterministicMNManager->GetListForBlock(pindexPrev->GetBlockHash()); auto dmn = mnList.GetMN(ptx.proTxHash); if (!dmn) { return state.DoS(100, false, REJECT_INVALID, "bad-protx-hash"); } // don't allow reuse of payee key for other keys (don't allow people to put the payee key onto an online server) if (payoutDest == CTxDestination(dmn->pdmnState->keyIDOwner) || payoutDest == CTxDestination(ptx.keyIDVoting)) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee-reuse"); } Coin coin; if (!GetUTXOCoin(dmn->collateralOutpoint, coin)) { // this should never happen (there would be no dmn otherwise) return state.DoS(100, false, REJECT_INVALID, "bad-protx-collateral"); } // don't allow reuse of collateral key for other keys (don't allow people to put the collateral key onto an online server) CTxDestination collateralTxDest; if (!ExtractDestination(coin.out.scriptPubKey, collateralTxDest)) { return state.DoS(100, false, REJECT_INVALID, "bad-protx-collateral-dest"); } if (collateralTxDest == CTxDestination(dmn->pdmnState->keyIDOwner) || collateralTxDest == CTxDestination(ptx.keyIDVoting)) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-reuse"); } if (mnList.HasUniqueProperty(ptx.pubKeyOperator)) { auto otherDmn = mnList.GetUniquePropertyMN(ptx.pubKeyOperator); if (ptx.proTxHash != otherDmn->proTxHash) { return state.DoS(10, false, REJECT_DUPLICATE, "bad-protx-dup-key"); } } if (!deterministicMNManager->IsDIP3Enforced(pindexPrev->nHeight)) { if (dmn->pdmnState->keyIDOwner != ptx.keyIDVoting) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-key-not-same"); } } if (!CheckInputsHash(tx, ptx, state)) { return false; } if (!CheckHashSig(ptx, dmn->pdmnState->keyIDOwner, state)) { return false; } } return true; }