diff --git a/src/evo/cbtx.cpp b/src/evo/cbtx.cpp index 9eb014a814b7..0ea054809917 100644 --- a/src/evo/cbtx.cpp +++ b/src/evo/cbtx.cpp @@ -4,10 +4,8 @@ #include #include -#include #include #include -#include #include #include #include @@ -45,93 +43,23 @@ bool CheckCbTx(const CCbTx& cbTx, const CBlockIndex* pindexPrev, TxValidationSta return true; } -// This can only be done after the block has been fully processed, as otherwise we won't have the finished MN list bool CheckCbTxMerkleRoots(const CBlock& block, const CCbTx& cbTx, const CBlockIndex* pindex, - const llmq::CQuorumBlockProcessor& quorum_block_processor, CSimplifiedMNList&& sml, - BlockValidationState& state) + const llmq::CQuorumBlockProcessor& quorum_block_processor, BlockValidationState& state) { - if (pindex) { - static int64_t nTimeMerkleMNL = 0; - static int64_t nTimeMerkleQuorum = 0; - - int64_t nTime1 = GetTimeMicros(); + if (pindex && cbTx.nVersion >= CCbTx::Version::MERKLE_ROOT_QUORUMS) { uint256 calculatedMerkleRoot; - if (!CalcCbTxMerkleRootMNList(calculatedMerkleRoot, std::move(sml), state)) { + if (!CalcCbTxMerkleRootQuorums(block, pindex->pprev, quorum_block_processor, calculatedMerkleRoot, state)) { // pass the state returned by the function above return false; } - if (calculatedMerkleRoot != cbTx.merkleRootMNList) { - return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-mnmerkleroot"); - } - - int64_t nTime2 = GetTimeMicros(); - nTimeMerkleMNL += nTime2 - nTime1; - LogPrint(BCLog::BENCHMARK, " - CalcCbTxMerkleRootMNList: %.2fms [%.2fs]\n", 0.001 * (nTime2 - nTime1), - nTimeMerkleMNL * 0.000001); - - if (cbTx.nVersion >= CCbTx::Version::MERKLE_ROOT_QUORUMS) { - if (!CalcCbTxMerkleRootQuorums(block, pindex->pprev, quorum_block_processor, calculatedMerkleRoot, state)) { - // pass the state returned by the function above - return false; - } - if (calculatedMerkleRoot != cbTx.merkleRootQuorums) { - return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-quorummerkleroot"); - } + if (calculatedMerkleRoot != cbTx.merkleRootQuorums) { + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-quorummerkleroot"); } - - int64_t nTime3 = GetTimeMicros(); - nTimeMerkleQuorum += nTime3 - nTime2; - LogPrint(BCLog::BENCHMARK, " - CalcCbTxMerkleRootQuorums: %.2fms [%.2fs]\n", 0.001 * (nTime3 - nTime2), - nTimeMerkleQuorum * 0.000001); } return true; } -bool CalcCbTxMerkleRootMNList(uint256& merkleRootRet, CSimplifiedMNList&& sml, BlockValidationState& state) -{ - try { - static std::atomic nTimeMerkle = 0; - - int64_t nTime1 = GetTimeMicros(); - - static Mutex cached_mutex; - static CSimplifiedMNList smlCached GUARDED_BY(cached_mutex); - static uint256 merkleRootCached GUARDED_BY(cached_mutex); - static bool mutatedCached GUARDED_BY(cached_mutex) {false}; - - LOCK(cached_mutex); - if (sml == smlCached) { - merkleRootRet = merkleRootCached; - if (mutatedCached) { - return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "mutated-cached-calc-cb-mnmerkleroot"); - } - return true; - } - - bool mutated = false; - merkleRootRet = sml.CalcMerkleRoot(&mutated); - - int64_t nTime2 = GetTimeMicros(); - nTimeMerkle += nTime2 - nTime1; - LogPrint(BCLog::BENCHMARK, " - CalcMerkleRoot: %.2fms [%.2fs]\n", 0.001 * (nTime2 - nTime1), - nTimeMerkle * 0.000001); - - smlCached = std::move(sml); - merkleRootCached = merkleRootRet; - mutatedCached = mutated; - - if (mutated) { - return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "mutated-calc-cb-mnmerkleroot"); - } - - return true; - } catch (const std::exception& e) { - LogPrintf("%s -- failed: %s\n", __func__, e.what()); - return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "failed-calc-cb-mnmerkleroot"); - } -} - using QcHashMap = std::map>; using QcIndexedHashMap = std::map>; @@ -288,129 +216,6 @@ bool CalcCbTxMerkleRootQuorums(const CBlock& block, const CBlockIndex* pindexPre return true; } -bool CheckCbTxBestChainlock(const CCbTx& cbTx, const CBlockIndex* pindex, - const llmq::CChainLocksHandler& chainlock_handler, BlockValidationState& state) -{ - if (cbTx.nVersion < CCbTx::Version::CLSIG_AND_BALANCE) { - return true; - } - - static Mutex cached_mutex; - static const CBlockIndex* cached_pindex GUARDED_BY(cached_mutex){nullptr}; - static std::optional> cached_chainlock GUARDED_BY(cached_mutex){std::nullopt}; - - auto best_clsig = chainlock_handler.GetBestChainLock(); - if (best_clsig.getHeight() == pindex->nHeight - 1 && cbTx.bestCLHeightDiff == 0 && cbTx.bestCLSignature == best_clsig.getSig()) { - // matches our best clsig which still hold values for the previous block - LOCK(cached_mutex); - cached_chainlock = std::make_pair(cbTx.bestCLSignature, cbTx.bestCLHeightDiff); - cached_pindex = pindex; - return true; - } - - std::optional> prevBlockCoinbaseChainlock{std::nullopt}; - if (LOCK(cached_mutex); cached_pindex == pindex->pprev) { - prevBlockCoinbaseChainlock = cached_chainlock; - } - if (!prevBlockCoinbaseChainlock.has_value()) { - prevBlockCoinbaseChainlock = GetNonNullCoinbaseChainlock(pindex->pprev); - } - // If std::optional prevBlockCoinbaseChainlock is empty, then up to the previous block, coinbase Chainlock is null. - if (prevBlockCoinbaseChainlock.has_value()) { - // Previous block Coinbase has a non-null Chainlock: current block's Chainlock must be non-null and at least as new as the previous one - if (!cbTx.bestCLSignature.IsValid()) { - // IsNull() doesn't exist for CBLSSignature: we assume that a non valid BLS sig is null - return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-null-clsig"); - } - if (cbTx.bestCLHeightDiff > prevBlockCoinbaseChainlock.value().second + 1) { - return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-older-clsig"); - } - } - - // IsNull() doesn't exist for CBLSSignature: we assume that a valid BLS sig is non-null - if (cbTx.bestCLSignature.IsValid()) { - int curBlockCoinbaseCLHeight = pindex->nHeight - static_cast(cbTx.bestCLHeightDiff) - 1; - if (best_clsig.getHeight() == curBlockCoinbaseCLHeight && best_clsig.getSig() == cbTx.bestCLSignature) { - // matches our best (but outdated) clsig, no need to verify it again - LOCK(cached_mutex); - cached_chainlock = std::make_pair(cbTx.bestCLSignature, cbTx.bestCLHeightDiff); - cached_pindex = pindex; - return true; - } - uint256 curBlockCoinbaseCLBlockHash = pindex->GetAncestor(curBlockCoinbaseCLHeight)->GetBlockHash(); - if (chainlock_handler.VerifyChainLock(llmq::CChainLockSig(curBlockCoinbaseCLHeight, curBlockCoinbaseCLBlockHash, cbTx.bestCLSignature)) != llmq::VerifyRecSigStatus::Valid) { - return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-invalid-clsig"); - } - LOCK(cached_mutex); - cached_chainlock = std::make_pair(cbTx.bestCLSignature, cbTx.bestCLHeightDiff); - cached_pindex = pindex; - } else if (cbTx.bestCLHeightDiff != 0) { - // Null bestCLSignature is allowed only with bestCLHeightDiff = 0 - return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-cldiff"); - } - - return true; -} - -bool CalcCbTxBestChainlock(const llmq::CChainLocksHandler& chainlock_handler, const CBlockIndex* pindexPrev, - uint32_t& bestCLHeightDiff, CBLSSignature& bestCLSignature) -{ - auto best_clsig = chainlock_handler.GetBestChainLock(); - if (best_clsig.getHeight() < Params().GetConsensus().DeploymentHeight(Consensus::DEPLOYMENT_V19)) { - // We don't want legacy BLS ChainLocks in CbTx (can happen on regtest/devenets) - best_clsig = llmq::CChainLockSig{}; - } - if (best_clsig.getHeight() == pindexPrev->nHeight) { - // Our best CL is the newest one possible - bestCLHeightDiff = 0; - bestCLSignature = best_clsig.getSig(); - return true; - } - - auto prevBlockCoinbaseChainlock = GetNonNullCoinbaseChainlock(pindexPrev); - if (prevBlockCoinbaseChainlock.has_value()) { - // Previous block Coinbase contains a non-null CL: We must insert the same sig or a better (newest) one - if (best_clsig.IsNull()) { - // We don't know any CL, therefore inserting the CL of the previous block - bestCLHeightDiff = prevBlockCoinbaseChainlock->second + 1; - bestCLSignature = prevBlockCoinbaseChainlock->first; - return true; - } - - // We check if our best CL is newer than the one from previous block Coinbase - int curCLHeight = best_clsig.getHeight(); - int prevCLHeight = pindexPrev->nHeight - static_cast(prevBlockCoinbaseChainlock->second) - 1; - if (curCLHeight < prevCLHeight) { - // Our best CL isn't newer: inserting CL from previous block - bestCLHeightDiff = prevBlockCoinbaseChainlock->second + 1; - bestCLSignature = prevBlockCoinbaseChainlock->first; - } - else { - // Our best CL is newer - bestCLHeightDiff = pindexPrev->nHeight - best_clsig.getHeight(); - bestCLSignature = best_clsig.getSig(); - } - - return true; - } - else { - // Previous block Coinbase has no CL. We can either insert null or any valid CL - if (best_clsig.IsNull()) { - // We don't know any CL, therefore inserting a null CL - bestCLHeightDiff = 0; - bestCLSignature.Reset(); - return false; - } - - // Inserting our best CL - bestCLHeightDiff = pindexPrev->nHeight - best_clsig.getHeight(); - bestCLSignature = chainlock_handler.GetBestChainLock().getSig(); - - return true; - } -} - - std::string CCbTx::ToString() const { return strprintf("CCbTx(nVersion=%d, nHeight=%d, merkleRootMNList=%s, merkleRootQuorums=%s, bestCLHeightDiff=%d, bestCLSig=%s, creditPoolBalance=%d.%08d)", diff --git a/src/evo/cbtx.h b/src/evo/cbtx.h index 7db9c2409b8d..32913eb89780 100644 --- a/src/evo/cbtx.h +++ b/src/evo/cbtx.h @@ -15,10 +15,7 @@ class BlockValidationState; class CBlock; class CBlockIndex; class TxValidationState; -class CSimplifiedMNList; - namespace llmq { -class CChainLocksHandler; class CQuorumBlockProcessor; }// namespace llmq @@ -66,19 +63,13 @@ template<> struct is_serializable_enum : std::true_type {}; bool CheckCbTx(const CCbTx& cbTx, const CBlockIndex* pindexPrev, TxValidationState& state); +// This can only be done after the block has been fully processed, as otherwise we won't have the finished MN list bool CheckCbTxMerkleRoots(const CBlock& block, const CCbTx& cbTx, const CBlockIndex* pindex, - const llmq::CQuorumBlockProcessor& quorum_block_processor, CSimplifiedMNList&& sml, - BlockValidationState& state); -bool CalcCbTxMerkleRootMNList(uint256& merkleRootRet, CSimplifiedMNList&& sml, BlockValidationState& state); + const llmq::CQuorumBlockProcessor& quorum_block_processor, BlockValidationState& state); bool CalcCbTxMerkleRootQuorums(const CBlock& block, const CBlockIndex* pindexPrev, const llmq::CQuorumBlockProcessor& quorum_block_processor, uint256& merkleRootRet, BlockValidationState& state); -bool CheckCbTxBestChainlock(const CCbTx& cbTx, const CBlockIndex* pindexPrev, - const llmq::CChainLocksHandler& chainlock_handler, BlockValidationState& state); -bool CalcCbTxBestChainlock(const llmq::CChainLocksHandler& chainlock_handler, const CBlockIndex* pindexPrev, - uint32_t& bestCLHeightDiff, CBLSSignature& bestCLSignature); - std::optional> GetNonNullCoinbaseChainlock(const CBlockIndex* pindex); #endif // BITCOIN_EVO_CBTX_H diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index 7eecfd6fbbb2..25e744cbe8d7 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -9,8 +9,6 @@ #include #include #include -#include -#include #include #include @@ -22,8 +20,8 @@ #include #include #include +#include #include -#include #include #include @@ -590,8 +588,7 @@ void CDeterministicMNList::RemoveMN(const uint256& proTxHash) } bool CDeterministicMNManager::ProcessBlock(const CBlock& block, gsl::not_null pindex, - BlockValidationState& state, const CCoinsViewCache& view, - llmq::CQuorumSnapshotManager& qsnapman, const CDeterministicMNList& newList, + BlockValidationState& state, const CDeterministicMNList& newList, std::optional& updatesRet) { AssertLockHeld(::cs_main); @@ -697,338 +694,6 @@ void CDeterministicMNManager::UpdatedBlockTip(gsl::not_null tipIndex = pindex; } -bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, gsl::not_null pindexPrev, - BlockValidationState& state, const CCoinsViewCache& view, - CDeterministicMNList& mnListRet, - llmq::CQuorumSnapshotManager& qsnapman, bool debugLogs) -{ - int nHeight = pindexPrev->nHeight + 1; - - CDeterministicMNList oldList = GetListForBlock(pindexPrev); - 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(pindexPrev); - - // 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, [&pindexPrev, &newList](auto& 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) { - auto newState = std::make_shared(*dmn.pdmnState); - newState->UpdateConfirmedHash(dmn.proTxHash, pindexPrev->GetBlockHash()); - newList.UpdateMN(dmn.proTxHash, newState); - } - }); - - newList.DecreaseScores(); - - const bool isMNRewardReallocation{DeploymentActiveAfter(pindexPrev, Params().GetConsensus(), Consensus::DEPLOYMENT_MN_RR)}; - const bool is_v23_deployed{DeploymentActiveAfter(pindexPrev, Params().GetConsensus(), Consensus::DEPLOYMENT_V23)}; - - // we skip the coinbase - for (int i = 1; i < (int)block.vtx.size(); i++) { - const CTransaction& tx = *block.vtx[i]; - - if (!tx.IsSpecialTxVersion()) { - // only interested in special TXs - continue; - } - - if (tx.nType == TRANSACTION_PROVIDER_REGISTER) { - const auto opt_proTx = GetTxPayload(tx); - if (!opt_proTx) { - return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-payload"); - } - auto& proTx = *opt_proTx; - - auto dmn = std::make_shared(newList.GetTotalRegisteredCount(), proTx.nType); - 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; - CAmount expectedCollateral = GetMnType(proTx.nType).collat_amount; - if (!proTx.collateralOutpoint.hash.IsNull() && (!view.GetCoin(dmn->collateralOutpoint, coin) || coin.IsSpent() || coin.out.nValue != expectedCollateral)) { - // 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.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "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); - if (debugLogs) { - 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()); - } - } - - for (const NetInfoEntry& entry : proTx.netInfo->GetEntries()) { - if (const auto& service_opt{entry.GetAddrPort()}; service_opt.has_value()) { - const CService& service{service_opt.value()}; - if (newList.HasUniqueProperty(service)) { - return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-dup-netinfo-entry"); - } - } else { - return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-netinfo-entry"); - } - } - if (newList.HasUniqueProperty(proTx.keyIDOwner) || newList.HasUniqueProperty(proTx.pubKeyOperator)) { - return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-dup-key"); - } - - dmn->nOperatorReward = proTx.nOperatorReward; - - auto dmnState = std::make_shared(proTx); - dmnState->nRegisteredHeight = nHeight; - if (proTx.netInfo->IsEmpty()) { - // start in banned pdmnState as we need to wait for a ProUpServTx - dmnState->BanIfNotBanned(nHeight); - } - dmn->pdmnState = dmnState; - - newList.AddMN(dmn); - - if (debugLogs) { - 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) { - const auto opt_proTx = GetTxPayload(tx); - if (!opt_proTx) { - return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-payload"); - } - - for (const NetInfoEntry& entry : opt_proTx->netInfo->GetEntries()) { - if (const auto& service_opt{entry.GetAddrPort()}; service_opt.has_value()) { - const CService& service{service_opt.value()}; - if (newList.HasUniqueProperty(service) && - newList.GetUniquePropertyMN(service)->proTxHash != opt_proTx->proTxHash) { - return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-dup-netinfo-entry"); - } - } else { - return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-netinfo-entry"); - } - } - - auto dmn = newList.GetMN(opt_proTx->proTxHash); - if (!dmn) { - return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-hash"); - } - if (opt_proTx->nType != dmn->nType) { - return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-type-mismatch"); - } - if (!IsValidMnType(opt_proTx->nType)) { - return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-type"); - } - - auto newState = std::make_shared(*dmn->pdmnState); - if (is_v23_deployed) { - // Extended addresses support in v23 means that the version can be updated - newState->nVersion = opt_proTx->nVersion; - } - newState->netInfo = opt_proTx->netInfo; - newState->scriptOperatorPayout = opt_proTx->scriptOperatorPayout; - if (opt_proTx->nType == MnType::Evo) { - newState->platformNodeID = opt_proTx->platformNodeID; - newState->platformP2PPort = opt_proTx->platformP2PPort; - newState->platformHTTPPort = opt_proTx->platformHTTPPort; - } - if (newState->IsBanned()) { - // only revive when all keys are set - if (newState->pubKeyOperator != CBLSLazyPublicKey() && !newState->keyIDVoting.IsNull() && - !newState->keyIDOwner.IsNull()) { - newState->Revive(nHeight); - if (debugLogs) { - LogPrintf("CDeterministicMNManager::%s -- MN %s revived at height %d\n", - __func__, opt_proTx->proTxHash.ToString(), nHeight); - } - } - } - - newList.UpdateMN(opt_proTx->proTxHash, newState); - if (debugLogs) { - LogPrintf("CDeterministicMNManager::%s -- MN %s updated at height %d: %s\n", - __func__, opt_proTx->proTxHash.ToString(), nHeight, opt_proTx->ToString()); - } - } else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REGISTRAR) { - const auto opt_proTx = GetTxPayload(tx); - if (!opt_proTx) { - return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-payload"); - } - - auto dmn = newList.GetMN(opt_proTx->proTxHash); - if (!dmn) { - return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-hash"); - } - auto newState = std::make_shared(*dmn->pdmnState); - if (newState->pubKeyOperator != opt_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); - // we update pubKeyOperator here, make sure state version matches - // Make sure we don't accidentally downgrade the state version if using version after basic BLS - newState->nVersion = newState->nVersion > ProTxVersion::BasicBLS ? newState->nVersion : opt_proTx->nVersion; - newState->netInfo = NetInfoInterface::MakeNetInfo(newState->nVersion); - newState->pubKeyOperator = opt_proTx->pubKeyOperator; - } - newState->keyIDVoting = opt_proTx->keyIDVoting; - newState->scriptPayout = opt_proTx->scriptPayout; - - newList.UpdateMN(opt_proTx->proTxHash, newState); - - if (debugLogs) { - LogPrintf("CDeterministicMNManager::%s -- MN %s updated at height %d: %s\n", - __func__, opt_proTx->proTxHash.ToString(), nHeight, opt_proTx->ToString()); - } - } else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REVOKE) { - const auto opt_proTx = GetTxPayload(tx); - if (!opt_proTx) { - return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-payload"); - } - - auto dmn = newList.GetMN(opt_proTx->proTxHash); - if (!dmn) { - return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-hash"); - } - auto newState = std::make_shared(*dmn->pdmnState); - newState->ResetOperatorFields(); - newState->BanIfNotBanned(nHeight); - newState->nRevocationReason = opt_proTx->nReason; - - newList.UpdateMN(opt_proTx->proTxHash, newState); - - if (debugLogs) { - LogPrintf("CDeterministicMNManager::%s -- MN %s revoked operator key at height %d: %s\n", - __func__, opt_proTx->proTxHash.ToString(), nHeight, opt_proTx->ToString()); - } - } else if (tx.nType == TRANSACTION_QUORUM_COMMITMENT) { - const auto opt_qc = GetTxPayload(tx); - if (!opt_qc) { - return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-payload"); - } - if (!opt_qc->commitment.IsNull()) { - const auto& llmq_params_opt = Params().GetLLMQ(opt_qc->commitment.llmqType); - if (!llmq_params_opt.has_value()) { - return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-commitment-type"); - } - int qcnHeight = int(opt_qc->nHeight); - int quorumHeight = qcnHeight - (qcnHeight % llmq_params_opt->dkgInterval) + int(opt_qc->commitment.quorumIndex); - auto pQuorumBaseBlockIndex = pindexPrev->GetAncestor(quorumHeight); - if (!pQuorumBaseBlockIndex || pQuorumBaseBlockIndex->GetBlockHash() != opt_qc->commitment.quorumHash) { - // we should actually never get into this case as validation should have caught it...but let's be sure - return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-quorum-hash"); - } - - HandleQuorumCommitment(opt_qc->commitment, pQuorumBaseBlockIndex, newList, qsnapman, debugLogs); - } - } - } - - // 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); - - if (debugLogs) { - 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()); - } - } - } - } - - // 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 (auto dmn = payee ? newList.GetMN(payee->proTxHash) : nullptr) { - auto newState = std::make_shared(*dmn->pdmnState); - newState->nLastPaidHeight = nHeight; - // Starting from v19 and until MNRewardReallocation, EvoNodes will be paid 4 blocks in a row - // No need to check if v19 is active, since EvoNode ProRegTxes are allowed only after v19 activation - // Note: If the payee wasn't found in the current block that's fine - if (dmn->nType == MnType::Evo && !isMNRewardReallocation) { - ++newState->nConsecutivePayments; - if (debugLogs) { - LogPrint(BCLog::MNPAYMENTS, "CDeterministicMNManager::%s -- MN %s is an EvoNode, bumping nConsecutivePayments to %d\n", - __func__, dmn->proTxHash.ToString(), newState->nConsecutivePayments); - } - } - newList.UpdateMN(payee->proTxHash, newState); - if (debugLogs) { - dmn = newList.GetMN(payee->proTxHash); - // Since the previous GetMN query returned a value, after an update, querying the same - // hash *must* give us a result. If it doesn't, that would be a potential logic bug. - assert(dmn); - LogPrint(BCLog::MNPAYMENTS, "CDeterministicMNManager::%s -- MN %s, nConsecutivePayments=%d\n", - __func__, dmn->proTxHash.ToString(), dmn->pdmnState->nConsecutivePayments); - } - } - - // reset nConsecutivePayments on non-paid EvoNodes - auto newList2 = newList; - newList2.ForEachMN(false, [&](auto& dmn) { - if (dmn.nType != MnType::Evo) return; - if (payee != nullptr && dmn.proTxHash == payee->proTxHash && !isMNRewardReallocation) return; - if (dmn.pdmnState->nConsecutivePayments == 0) return; - if (debugLogs) { - LogPrint(BCLog::MNPAYMENTS, "CDeterministicMNManager::%s -- MN %s, reset nConsecutivePayments %d->0\n", - __func__, dmn.proTxHash.ToString(), dmn.pdmnState->nConsecutivePayments); - } - auto newState = std::make_shared(*dmn.pdmnState); - newState->nConsecutivePayments = 0; - newList.UpdateMN(dmn.proTxHash, newState); - }); - - mnListRet = std::move(newList); - - return true; -} - -void CDeterministicMNManager::HandleQuorumCommitment(const llmq::CFinalCommitment& qc, - gsl::not_null pQuorumBaseBlockIndex, - CDeterministicMNList& mnList, - llmq::CQuorumSnapshotManager& qsnapman, bool debugLogs) -{ - // The commitment has already been validated at this point, so it's safe to use members of it - - auto members = llmq::utils::GetAllQuorumMembers(qc.llmqType, *this, qsnapman, pQuorumBaseBlockIndex); - - for (size_t i = 0; i < members.size(); i++) { - if (!mnList.HasMN(members[i]->proTxHash)) { - continue; - } - if (!qc.validMembers[i]) { - // punish MN for failed DKG participation - // The idea is to immediately ban a MN when it fails 2 DKG sessions with only a few blocks in-between - // If there were enough blocks between failures, the MN has a chance to recover as he reduces his penalty by 1 for every block - // If it however fails 3 times in the timespan of a single payment cycle, it should definitely get banned - mnList.PoSePunish(members[i]->proTxHash, mnList.CalcPenalty(66), debugLogs); - } - } -} - CDeterministicMNList CDeterministicMNManager::GetListForBlockInternal(gsl::not_null pindex) { CDeterministicMNList snapshot; diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h index 3b0eee1bfbba..6987d8448b8d 100644 --- a/src/evo/deterministicmns.h +++ b/src/evo/deterministicmns.h @@ -34,11 +34,6 @@ class TxValidationState; extern RecursiveMutex cs_main; -namespace llmq { -class CFinalCommitment; -class CQuorumSnapshotManager; -} // namespace llmq - class CDeterministicMN { private: @@ -563,20 +558,12 @@ class CDeterministicMNManager ~CDeterministicMNManager() = default; bool ProcessBlock(const CBlock& block, gsl::not_null pindex, BlockValidationState& state, - const CCoinsViewCache& view, llmq::CQuorumSnapshotManager& qsnapman, const CDeterministicMNList& newList, std::optional& updatesRet) EXCLUSIVE_LOCKS_REQUIRED(!cs, ::cs_main); bool UndoBlock(gsl::not_null pindex, std::optional& updatesRet) EXCLUSIVE_LOCKS_REQUIRED(!cs); void UpdatedBlockTip(gsl::not_null pindex) EXCLUSIVE_LOCKS_REQUIRED(!cs); - // 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, gsl::not_null pindexPrev, - BlockValidationState& state, const CCoinsViewCache& view, CDeterministicMNList& mnListRet, - llmq::CQuorumSnapshotManager& qsnapman, bool debugLogs) EXCLUSIVE_LOCKS_REQUIRED(!cs); - void HandleQuorumCommitment(const llmq::CFinalCommitment& qc, gsl::not_null pQuorumBaseBlockIndex, - CDeterministicMNList& mnList, llmq::CQuorumSnapshotManager& qsnapman, bool debugLogs); - CDeterministicMNList GetListForBlock(gsl::not_null pindex) EXCLUSIVE_LOCKS_REQUIRED(!cs) { LOCK(cs); return GetListForBlockInternal(pindex); diff --git a/src/evo/dmnstate.cpp b/src/evo/dmnstate.cpp index dd95a8b5ee4d..70a1c139c9c9 100644 --- a/src/evo/dmnstate.cpp +++ b/src/evo/dmnstate.cpp @@ -8,7 +8,6 @@ #include #include #include