diff --git a/src/main.cpp b/src/main.cpp index fb6d5c3e9..d033a870c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2124,7 +2124,7 @@ bool AcceptToMemoryPoolInt(CTxMemPool& pool, CValidationState &state, const CTra ContextualCheckInputs(tx, state, view, nextBlockHeight, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata, Params().GetConsensus(), consensusBranchId); if ( flag != 0 ) KOMODO_CONNECTING = -1; - return error("AcceptToMemoryPool: BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags %s", hash.ToString()); + return error("AcceptToMemoryPool: ConnectInputs failed against MANDATORY but not STANDARD flags %s", hash.ToString()); } // if this is a valid stake transaction, don't put it in the mempool diff --git a/src/miner.cpp b/src/miner.cpp index 6e29d167d..1613c5629 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -1977,11 +1977,11 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const std::vecto mempool.removeConflicts(notarizationTx, removedTxVec); CValidationState mempoolState; relayTx = myAddtomempool(notarizationTx, &state); - if (LogAcceptCategory("notarizationverbose") || LogAcceptCategory("notarization")) + if (LogAcceptCategory("notarization")) { for (auto oneTx : removedTxVec) { - if (LogAcceptCategory("notarizationverbose")) + if (LogAcceptCategory("verbose")) { UniValue jsonNTx(UniValue::VOBJ); TxToUniv(oneTx, uint256(), jsonNTx); diff --git a/src/pbaas/notarization.cpp b/src/pbaas/notarization.cpp index 9bb8472be..ff6a0d05d 100644 --- a/src/pbaas/notarization.cpp +++ b/src/pbaas/notarization.cpp @@ -3967,8 +3967,20 @@ bool CPBaaSNotarization::ConfirmOrRejectNotarizations(CWallet *pWallet, { COptCCParams tP; if (oneEvidenceSpend.scriptPubKey.IsPayToCryptoCondition(tP) && + tP.IsValid() && tP.evalCode == EVAL_NOTARY_EVIDENCE || tP.evalCode == EVAL_FINALIZE_NOTARIZATION) { + // if our evidence include an already confirmed finalization for the same output, + // abort with nothing to do + CObjectFinalization tPOF; + if (tP.evalCode == EVAL_FINALIZE_NOTARIZATION && + tP.vData.size() && + (tPOF = CObjectFinalization(tP.vData[0])).IsValid() && + tPOF.output == of.output && + tPOF.IsConfirmed()) + { + return state.Error(errorPrefix + "target notarization already confirmed"); + } of.evidenceInputs.push_back(txBuilder.mtx.vin.size()); if (!inputSet.count(oneEvidenceSpend.txIn.prevout)) { @@ -4021,8 +4033,30 @@ bool CPBaaSNotarization::ConfirmOrRejectNotarizations(CWallet *pWallet, auto newTxBuilder = TransactionBuilder(Params().GetConsensus(), nHeight, pWallet); TransactionBuilder &oneConfirmedBuilder = makeInputTx ? newTxBuilder : txBuilder; + bool isConfirmed = false; for (auto &oneInput : spendsToClose[oneConfirmedIdx]) { + COptCCParams tP; + if (oneInput.scriptPubKey.IsPayToCryptoCondition(tP) && + tP.IsValid() && + tP.evalCode == EVAL_NOTARY_EVIDENCE || tP.evalCode == EVAL_FINALIZE_NOTARIZATION) + { + // if our evidence include an already confirmed finalization for the same output, + // abort with nothing to do + CObjectFinalization tPOF; + if (tP.evalCode == EVAL_FINALIZE_NOTARIZATION && + tP.vData.size() && + (tPOF = CObjectFinalization(tP.vData[0])).IsValid() && + ((tPOF.output.hash.IsNull() && + oneInput.txIn.prevout.hash == cnd.vtx[oneConfirmedIdx].first.hash && + tPOF.output.n == cnd.vtx[oneConfirmedIdx].first.n) || + tPOF.output == cnd.vtx[oneConfirmedIdx].first) && + tPOF.IsConfirmed()) + { + isConfirmed = true; + } + } + if (!inputSet.count(oneInput.txIn.prevout)) { inputSet.insert(oneInput.txIn.prevout); @@ -4038,11 +4072,14 @@ bool CPBaaSNotarization::ConfirmOrRejectNotarizations(CWallet *pWallet, if (makeInputTx) { - CObjectFinalization oneConfirmedFinalization = CObjectFinalization(CObjectFinalization::FINALIZE_NOTARIZATION, - SystemID, - cnd.vtx[oneConfirmedIdx].first.hash, - cnd.vtx[oneConfirmedIdx].first.n, - height); + CObjectFinalization oneConfirmedFinalization = + CObjectFinalization(isConfirmed ? + CObjectFinalization::FINALIZE_NOTARIZATION + CObjectFinalization::FINALIZE_CONFIRMED : + CObjectFinalization::FINALIZE_NOTARIZATION, + SystemID, + cnd.vtx[oneConfirmedIdx].first.hash, + cnd.vtx[oneConfirmedIdx].first.n, + height); //oneConfirmedFinalization.evidenceInputs = evidenceInputs; cp = CCinit(&CC, EVAL_FINALIZE_NOTARIZATION); @@ -5332,6 +5369,12 @@ bool ValidateFinalizeNotarization(struct CCcontract_info *cp, Eval* eval, const return true; } } + if (LogAcceptCategory("notarization") && LogAcceptCategory("verbose")) + { + UniValue jsonNTx(UniValue::VOBJ); + TxToUniv(tx, uint256(), jsonNTx); + LogPrintf("%s: Invalid spend of confirmed finalization on transaction:\n%s\n", __func__, jsonNTx.write(1,2).c_str()); + } return eval->Error("Invalid spend of confirmed finalization to transaction with no confirmed output"); } diff --git a/src/pbaas/reserves.h b/src/pbaas/reserves.h index 7436bb90c..42f61fbb1 100644 --- a/src/pbaas/reserves.h +++ b/src/pbaas/reserves.h @@ -947,6 +947,8 @@ public: CCurrencyValueMap totalAmounts; // total amount exported of each currency, including fees CCurrencyValueMap totalFees; // total fees in all currencies to split between this export and import CCurrencyValueMap totalBurned; // if this is a cross chain export, some currencies will be burned, the rest held in deposits + + // TODO: HARDENING - on next testnet reset, move this variable to before the CCurrencyValueMaps for easier parsing in the contracts CTransferDestination exporter; // typically the exporting miner or staker's address, to accept deferred payment for the export int32_t firstInput; // if export is from inputs, on chain of reserveTransfers, this is first input, -1 for cross-chain