From 181ccf78ea30e46cca4672b92a09f20b2fd2419f Mon Sep 17 00:00:00 2001 From: miketout Date: Mon, 19 Jun 2023 22:17:04 -0700 Subject: [PATCH] Double reorg proof fix --- src/pbaas/notarization.cpp | 286 +++++++++++++++++++++++++++++++++++-- src/pbaas/pbaas.h | 10 ++ 2 files changed, 286 insertions(+), 10 deletions(-) diff --git a/src/pbaas/notarization.cpp b/src/pbaas/notarization.cpp index 4416ae7f40e..185a8abcc6f 100644 --- a/src/pbaas/notarization.cpp +++ b/src/pbaas/notarization.cpp @@ -2461,12 +2461,65 @@ std::tuple GetPriorReferen } else { - CObjectFinalization lastOf; + std::vector lastOfs; + std::vector lastNotarizationIndexes; lastLocalNotarization = lastNotarization; - if (!lastLocalNotarization.FindEarnedNotarization(lastOf, &lastNotarizationAddressEntry)) + if (!lastLocalNotarization.FindEarnedNotarizations(lastOfs, &lastNotarizationIndexes) || + !lastNotarizationIndexes.size()) { lastLocalNotarization = lastNotarization = CPBaaSNotarization(); } + else if (lastNotarizationIndexes.size() > 1) + { + std::set evidenceCheckTypes; + + // we need to select between multiple priors, and to do that, we + // see if there is commitment/challenge evidence that matches one by checking if the + // commitments match the respective entropyhash + evidenceCheckTypes.insert(CHAINOBJ_COMMITMENTDATA); + CCrossChainProof commitmentProof(evidence.GetSelectEvidence(evidenceCheckTypes)); + + evidenceCheckTypes.clear(); + evidenceCheckTypes.insert(CHAINOBJ_PROOF_ROOT); + CCrossChainProof rootProof(evidence.GetSelectEvidence(evidenceCheckTypes)); + + if (commitmentProof.chainObjects.size() && + rootProof.chainObjects.size()) + { + CTransaction notarizationTx; + uint256 nBlkHash; + // make sure the one we find has a matching proof + for (auto &oneIndex : lastNotarizationIndexes) + { + if (!myGetTransaction(oneIndex.first.txhash, notarizationTx, nBlkHash)) + { + LogPrint("notarization", "Error retrieving notarization transaction\n"); + continue; + } + std::vector<__uint128_t> checkCommitments; + uint256 blockTypes = ((CChainObject *)commitmentProof.chainObjects[0])->object.GetSmallCommitments(checkCommitments); + if (lastLocalNotarization.CheckEntropyHashMatch(nBlkHash, + checkCommitments, + lastLocalNotarization.currencyID, + (lastLocalNotarization.proofRoots.count(lastLocalNotarization.currencyID) ? + lastLocalNotarization.proofRoots[lastLocalNotarization.currencyID].rootHeight : + 1), + ((CChainObject *)rootProof.chainObjects[0])->object.rootHeight)) + { + lastNotarizationAddressEntry = oneIndex; + break; + } + } + } + else + { + lastNotarizationAddressEntry = lastNotarizationIndexes.back(); + } + } + else + { + lastNotarizationAddressEntry = lastNotarizationIndexes.back(); + } } std::get<2>(retVal) = CUTXORef(lastNotarizationAddressEntry.first.txhash, lastNotarizationAddressEntry.first.index); @@ -3026,9 +3079,9 @@ CPBaaSNotarization IsValidPrimaryChainEvidence(const CCurrencyDefinition &extern if (ccProof.chainObjects.size() > 2 && ((CChainObject *)ccProof.chainObjects[0])->object.vdxfd == CNotaryEvidence::PrimaryProofKey() && ccProof.chainObjects[1]->objectType == CHAINOBJ_PROOF_ROOT && - ((CChainObject *)ccProof.chainObjects[0])->object != provenNotarization.proofRoots[provenNotarization.currencyID]) + ((CChainObject *)ccProof.chainObjects[1])->object != provenNotarization.proofRoots[provenNotarization.currencyID]) { - challengeProofRoot = ((CChainObject *)ccProof.chainObjects[0])->object; + challengeProofRoot = ((CChainObject *)ccProof.chainObjects[1])->object; continue; } else if (crossChainProofCount < 2 && // must be first, added on final confirmation @@ -3128,10 +3181,9 @@ CPBaaSNotarization IsValidPrimaryChainEvidence(const CCurrencyDefinition &extern lastNotarization.proofRoots.count(ASSETCHAINS_CHAINID) && checkHeight <= chainActive.Height() && (lastNotarization.IsBlockOneNotarization() || - (provenNotarization.proofRoots[ASSETCHAINS_CHAINID].rootHeight - + ((provenNotarization.proofRoots[ASSETCHAINS_CHAINID].rootHeight - lastNotarization.proofRoots[ASSETCHAINS_CHAINID].rootHeight) >= - (chainActive[provenNotarization.proofRoots[ASSETCHAINS_CHAINID].rootHeight]->nBits >= - CPBaaSNotarization::GetAdjustedNotarizationModulo(ConnectedChains.ThisChain().blockNotarizationModulo, + CPBaaSNotarization::GetAdjustedNotarizationModulo(ConnectedChains.ThisChain().blockNotarizationModulo, lastConfirmedHeight, height)))) { @@ -3175,9 +3227,68 @@ CPBaaSNotarization IsValidPrimaryChainEvidence(const CCurrencyDefinition &extern } else { - CObjectFinalization lastOf; + std::vector lastOfs; + std::vector lastNotarizationIndexes; lastLocalNotarization = lastNotarization; - if (!lastLocalNotarization.FindEarnedNotarization(lastOf, &lastNotarizationAddressEntry)) + + if (!lastLocalNotarization.FindEarnedNotarizations(lastOfs, &lastNotarizationIndexes) || + !lastNotarizationIndexes.size()) + { + lastLocalNotarization = lastNotarization = CPBaaSNotarization(); + } + else if (lastNotarizationIndexes.size() > 1) + { + std::set evidenceCheckTypes; + + // we need to select between multiple priors, and to do that, we + // see if there is commitment/challenge evidence that matches one by checking if the + // commitments match the respective entropyhash + evidenceCheckTypes.insert(CHAINOBJ_COMMITMENTDATA); + CCrossChainProof commitmentProof(evidence.GetSelectEvidence(evidenceCheckTypes)); + + evidenceCheckTypes.clear(); + evidenceCheckTypes.insert(CHAINOBJ_PROOF_ROOT); + CCrossChainProof rootProof(evidence.GetSelectEvidence(evidenceCheckTypes)); + + if (commitmentProof.chainObjects.size() && + rootProof.chainObjects.size()) + { + futureProofRoot = ((CChainObject *)rootProof.chainObjects[0])->object; + CTransaction notarizationTx; + uint256 nBlkHash; + // make sure the one we find has a matching proof + for (auto &oneIndex : lastNotarizationIndexes) + { + if (!myGetTransaction(oneIndex.first.txhash, notarizationTx, nBlkHash)) + { + LogPrint("notarization", "Error retrieving notarization transaction\n"); + continue; + } + std::vector<__uint128_t> checkCommitments; + uint256 blockTypes = ((CChainObject *)commitmentProof.chainObjects[0])->object.GetSmallCommitments(checkCommitments); + if (lastLocalNotarization.CheckEntropyHashMatch(nBlkHash, + checkCommitments, + lastLocalNotarization.currencyID, + (lastLocalNotarization.proofRoots.count(lastLocalNotarization.currencyID) ? + lastLocalNotarization.proofRoots[lastLocalNotarization.currencyID].rootHeight : + 1), + futureProofRoot.rootHeight)) + { + lastNotarizationAddressEntry = oneIndex; + } + } + } + else + { + lastNotarizationAddressEntry = lastNotarizationIndexes.back(); + } + } + else + { + lastNotarizationAddressEntry = lastNotarizationIndexes.back(); + } + + if (!lastNotarizationIndexes.size()) { lastLocalNotarization = lastNotarization = CPBaaSNotarization(); } @@ -3422,7 +3533,7 @@ CPBaaSNotarization IsValidPrimaryChainEvidence(const CCurrencyDefinition &extern } else { - validBasicEvidence = (lastLocalNotarization.IsValid() && lastLocalNotarization.IsPreLaunch()) || !challengeProofRoot.IsValid(); + validBasicEvidence = lastLocalNotarization.IsValid() || !challengeProofRoot.IsValid(); proofState = validateEarned ? (validBasicEvidence ? EXPECT_NOTHING : EXPECT_COMMITMENT_PROOF) : EXPECT_FINALIZATION_PROOF; } } @@ -5188,6 +5299,38 @@ std::vector GetGoodNodes(int maxNum) return retVal; } +bool CPBaaSNotarization::CheckEntropyHashMatch(const uint256 &entropyHash, + const CHashCommitments &commitments, + const uint160 ¤cyID, + uint32_t startingHeight, + uint32_t endHeight) +{ + uint32_t rangeLen = endHeight - startingHeight; + + std::vector<__uint128_t> checkCommitments; + uint256 blockTypes = commitments.GetSmallCommitments(checkCommitments); + auto commitmentRanges = CPBaaSNotarization::GetBlockCommitmentRanges(startingHeight, endHeight, entropyHash); + + int checkIndex = 0; + bool mismatchIndex = false; + for (auto &oneRange : commitmentRanges) + { + for (uint32_t loop = oneRange.first; loop <= oneRange.second; loop++, checkIndex++) + { + if (loop != ((uint32_t)checkCommitments[checkIndex]) >> 1) + { + mismatchIndex = true; + break; + } + } + if (mismatchIndex) + { + break; + } + } + return !mismatchIndex; +} + std::vector> CPBaaSNotarization::GetBlockCommitmentRanges(uint32_t fromHeight, uint32_t toHeight, uint256 entropy) { @@ -8315,6 +8458,98 @@ bool CPBaaSNotarization::FindFinalizedIndexByVDXFKey(const uint160 ¬arization return true; } +bool CPBaaSNotarization::FindFinalizedIndexesByVDXFKey(const uint160 ¬arizationIdxKey, + std::vector &confirmedFinalization, + std::vector &earnedNotarizationIndex) +{ + bool retVal = false; + BlockMap::iterator blockIt; + std::vector addressIndex; + if (!GetAddressIndex(notarizationIdxKey, CScript::P2IDX, addressIndex) || !addressIndex.size()) + { + if (LogAcceptCategory("verbose")) + { + LogPrint("notarization", "No transaction data for index key - not necessarily an error\n"); + } + return retVal; + } + + CTransaction notarizationTx; + CPBaaSNotarization foundNotarization; + uint256 nBlkHash; + COptCCParams nP; + + for (auto &oneIndexEntry : addressIndex) + { + if (oneIndexEntry.first.spending) + { + continue; + } + if (!myGetTransaction(oneIndexEntry.first.txhash, notarizationTx, nBlkHash) || + oneIndexEntry.first.index >= notarizationTx.vout.size() || + nBlkHash.IsNull() || + (blockIt = mapBlockIndex.find(nBlkHash)) == mapBlockIndex.end() || + !chainActive.Contains(blockIt->second) || + !(notarizationTx.vout[oneIndexEntry.first.index].scriptPubKey.IsPayToCryptoCondition(nP) && + nP.IsValid() && + (nP.evalCode == EVAL_ACCEPTEDNOTARIZATION || nP.evalCode == EVAL_EARNEDNOTARIZATION) && + nP.vData.size() && + (foundNotarization = CPBaaSNotarization(nP.vData[0])).IsValid())) + { + LogPrint("notarization", "Valid notarization not found for index key\n"); + continue; + } + earnedNotarizationIndex.push_back(oneIndexEntry); + } + if (!earnedNotarizationIndex.size()) + { + LogPrint("notarization", "Unable to locate transaction for index key\n"); + return retVal; + } + + for (int i = 0; i < earnedNotarizationIndex.size(); i++) + { + confirmedFinalization.push_back(CObjectFinalization()); + uint160 finalizedNotarizationKey = CCrossChainRPCData::GetConditionID(CObjectFinalization::ObjectFinalizationFinalizedKey(), + earnedNotarizationIndex[i].first.txhash, + earnedNotarizationIndex[i].first.index); + + addressIndex.clear(); + if (!GetAddressIndex(finalizedNotarizationKey, CScript::P2IDX, addressIndex) || + !addressIndex.size()) + { + continue; + } + + CAddressIndexDbEntry finalizationIndex; + CTransaction finalizationTx; + uint256 blkHash; + COptCCParams fP; + + for (auto &oneIndexEntry : addressIndex) + { + if (oneIndexEntry.first.spending) + { + continue; + } + if (!myGetTransaction(finalizationIndex.first.txhash, finalizationTx, blkHash) || + finalizationIndex.first.index >= finalizationTx.vout.size() || + blkHash.IsNull() || + (blockIt = mapBlockIndex.find(blkHash)) == mapBlockIndex.end() || + !chainActive.Contains(blockIt->second) || + !(finalizationTx.vout[finalizationIndex.first.index].scriptPubKey.IsPayToCryptoCondition(fP) && + fP.IsValid() && + fP.evalCode == EVAL_FINALIZE_NOTARIZATION && + fP.vData.size() && + (confirmedFinalization[i] = CObjectFinalization(fP.vData[0])).IsValid())) + { + LogPrint("notarization", "Invalid finalization transaction for index key\n"); + } + } + } + return true; +} + bool CPBaaSNotarization::FindEarnedNotarization(CObjectFinalization &confirmedFinalization, CAddressIndexDbEntry *pEarnedNotarizationIndex) const { CAddressIndexDbEntry __earnedNotarizationIndex; @@ -8346,6 +8581,37 @@ bool CPBaaSNotarization::FindEarnedNotarization(CObjectFinalization &confirmedFi return FindFinalizedIndexByVDXFKey(notarizationIdxKey, confirmedFinalization, earnedNotarizationIndex); } +bool CPBaaSNotarization::FindEarnedNotarizations(std::vector &confirmedFinalizations, std::vector *pEarnedNotarizationIndex) const +{ + std::vector __earnedNotarizationIndex; + std::vector &earnedNotarizationIndex = pEarnedNotarizationIndex ? *pEarnedNotarizationIndex : __earnedNotarizationIndex; + + bool retVal = false; + CPBaaSNotarization checkNotarization = *this; + if (checkNotarization.IsMirror()) + { + if (!checkNotarization.SetMirror(false)) + { + LogPrintf("Unable to interpret notarization data for notarization:\n%s\n", checkNotarization.ToUniValue().write(1,2).c_str()); + printf("Unable to interpret notarization data for notarization:\n%s\n", checkNotarization.ToUniValue().write(1,2).c_str()); + return retVal; + } + } + + if (LogAcceptCategory("notarization") && LogAcceptCategory("verbose")) + { + std::vector checkHex = ::AsVector(checkNotarization); + LogPrintf("%s: hex of notarization: %s\n", __func__, HexBytes(&(checkHex[0]), checkHex.size()).c_str()); + printf("%s: hex of notarization: %s\n", __func__, HexBytes(&(checkHex[0]), checkHex.size()).c_str()); + } + + CNativeHashWriter hw; + hw << checkNotarization; + uint256 objHash = hw.GetHash(); + uint160 notarizationIdxKey = CCrossChainRPCData::GetConditionID(currencyID, CPBaaSNotarization::EarnedNotarizationKey(), objHash); + return FindFinalizedIndexesByVDXFKey(notarizationIdxKey, confirmedFinalizations, earnedNotarizationIndex); +} + // look for finalized notarizations either on chain or in the mempool, which are eligible for submission // and submit them to the notary chain, referring to the last on the notary chain that we agree with. std::vector CPBaaSNotarization::SubmitFinalizedNotarizations(const CRPCChainData &externalSystem, diff --git a/src/pbaas/pbaas.h b/src/pbaas/pbaas.h index f87b75a5847..2ee0559f5c1 100644 --- a/src/pbaas/pbaas.h +++ b/src/pbaas/pbaas.h @@ -653,9 +653,13 @@ class CPBaaSNotarization CPBaaSNotarization ¬arization); bool FindEarnedNotarization(CObjectFinalization &finalization, CAddressIndexDbEntry *pEarnedNotarizationIndex=nullptr) const; + bool FindEarnedNotarizations(std::vector &finalization, std::vector *pEarnedNotarizationIndex=nullptr) const; static bool FindFinalizedIndexByVDXFKey(const uint160 ¬arizationIdxKey, CObjectFinalization &confirmedFinalization, CAddressIndexDbEntry &earnedNotarizationIndex); + static bool FindFinalizedIndexesByVDXFKey(const uint160 ¬arizationIdxKey, + std::vector &confirmedFinalizations, + std::vector &earnedNotarizationIndex); bool CheckCrossNotarizationProgression(const CCurrencyDefinition &curDef, CPBaaSNotarization &priorNotarization, @@ -701,6 +705,12 @@ class CPBaaSNotarization return proofRootIt->second; } + static bool CheckEntropyHashMatch(const uint256 &entropyHash, + const CHashCommitments &commitments, + const uint160 ¤cyID, + uint32_t startingHeight, + uint32_t endHeight); + static std::vector> GetBlockCommitmentRanges(uint32_t lastNotarizationHeight, uint32_t currentNotarizationHeight, uint256 entropy);