diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 248c33298ba..5c3d4ac81ea 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,7 +7,7 @@ stages: ######################################################################################################################## variables: - VERSION: 0.9.9-2 + VERSION: 0.9.9-3 VERUS_CLI_ARM64_LINUX: Verus-CLI-Linux-v${VERSION}-arm64.tar.gz VERUS_CLI_LINUX_X86_64: Verus-CLI-Linux-v${VERSION}-x86_64.tar.gz diff --git a/README.md b/README.md index c49c6a0e361..f58fa11c31c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -## VerusCoin version 0.9.9-2 +## VerusCoin version 0.9.9-3 Arguably the world's most advanced technology, zero knowledge privacy-centric blockchain, Verus Coin brings Sapling performance and zero knowledge features to an intelligent system with interchain smart contracts and a completely original, combined proof of stake/proof of work consensus algorithm that solves the nothing at stake problem. With this and its approach towards CPU mining and ASICs, Verus Coin strives to be one of the most naturally decentralizing and attack resistant blockchains in existence. diff --git a/doc/man/verus-cli/linux/README.txt b/doc/man/verus-cli/linux/README.txt index 187b5371e7e..dd8f402943d 100644 --- a/doc/man/verus-cli/linux/README.txt +++ b/doc/man/verus-cli/linux/README.txt @@ -1,5 +1,5 @@ -VerusCoin Command Line Tools v0.9.9-2 +VerusCoin Command Line Tools v0.9.9-3 Contents: verusd - VerusCoin daemon diff --git a/doc/man/verus-cli/mac/README.txt b/doc/man/verus-cli/mac/README.txt index 52c039a9d18..272008ae160 100644 --- a/doc/man/verus-cli/mac/README.txt +++ b/doc/man/verus-cli/mac/README.txt @@ -1,5 +1,5 @@ -VerusCoin Command Line Tools v0.9.9-2 +VerusCoin Command Line Tools v0.9.9-3 Contents: verusd - VerusCoin daemon. diff --git a/doc/man/verus-cli/windows/README.txt b/doc/man/verus-cli/windows/README.txt index bb905b04c64..93c58a738b9 100644 --- a/doc/man/verus-cli/windows/README.txt +++ b/doc/man/verus-cli/windows/README.txt @@ -1,5 +1,5 @@ -VerusCoin Command Line Tools v0.9.9-2 +VerusCoin Command Line Tools v0.9.9-3 Contents: verusd.exe - VerusCoin daemon diff --git a/src/deprecation.h b/src/deprecation.h index 157ca4b0365..1f2508b42b5 100644 --- a/src/deprecation.h +++ b/src/deprecation.h @@ -9,7 +9,7 @@ // * Shut down 20 weeks' worth of blocks after the estimated release block height. // * A warning is shown during the 2 weeks' worth of blocks prior to shut down. -static const int APPROX_RELEASE_HEIGHT = 2451000; +static const int APPROX_RELEASE_HEIGHT = 2462000; static const int WEEKS_UNTIL_DEPRECATION = 20; static const int DEPRECATION_HEIGHT = APPROX_RELEASE_HEIGHT + (WEEKS_UNTIL_DEPRECATION * 7 * 60 * 24); diff --git a/src/key_io.cpp b/src/key_io.cpp index 12b46565434..127064aba67 100644 --- a/src/key_io.cpp +++ b/src/key_io.cpp @@ -120,6 +120,12 @@ UniValue getvdxfid_internal(const UniValue& params) cleanName = CleanName(vdxfName, parentID); vdxfID = GetDestinationID(idDest); } + else if (vdxfName.substr(0,2) == "0x" && !(vdxfID = CTransferDestination::DecodeEthDestination(vdxfName)).IsNull()) + { + parentIDName = "currencyaddresstype"; + parentID = CIdentity::GetID("veth", parentID); + cleanName = vdxfName; + } else { isIndexKey = true; diff --git a/src/komodo_globals.h b/src/komodo_globals.h index 0986f9decb8..cbcabc6f298 100644 --- a/src/komodo_globals.h +++ b/src/komodo_globals.h @@ -64,6 +64,8 @@ uint160 VERUS_CHAINID; std::string VERUS_CHAINNAME = "VRSC"; uint32_t PBAAS_TESTFORK_TIME = 1679072400; +const uint32_t PBAAS_PREMAINNET_ACTIVATION = 1679072400; // already activated, so harden with immutable value +std::string PBAAS_TEST_ETH_CONTRACT = "0x3fa3a60240ef59460f5b34e2ec5a06ab892a2d00"; bool PARAMS_LOADED = false; uint16_t ASSETCHAINS_P2PPORT, ASSETCHAINS_RPCPORT; diff --git a/src/main.cpp b/src/main.cpp index 3e33750be18..f293adb7952 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1431,7 +1431,7 @@ bool ContextualCheckTransaction( if (isPBaaS && (!IsVerusActive() || IsVerusMainnetActive() || - chainActive[std::min((uint32_t)chainActive.Height(), (uint32_t)nHeight)]->nTime > PBAAS_TESTFORK_TIME) && + chainActive[std::min((uint32_t)chainActive.Height(), (uint32_t)nHeight)]->nTime > PBAAS_PREMAINNET_ACTIVATION) && p.AsVector().size() >= CScript::MAX_SCRIPT_ELEMENT_SIZE) { if (LogAcceptCategory("notarization")) diff --git a/src/pbaas/crosschainrpc.cpp b/src/pbaas/crosschainrpc.cpp index 6c3eef75cf4..78765d7101e 100644 --- a/src/pbaas/crosschainrpc.cpp +++ b/src/pbaas/crosschainrpc.cpp @@ -50,7 +50,6 @@ extern string PBAAS_HOST; extern string PBAAS_USERPASS; extern int32_t PBAAS_PORT; extern std::string VERUS_CHAINNAME; -extern uint32_t PBAAS_TESTFORK_TIME; // // Exception thrown on connection error. This error is used to determine diff --git a/src/pbaas/crosschainrpc.h b/src/pbaas/crosschainrpc.h index d99aaada921..ede62d04793 100644 --- a/src/pbaas/crosschainrpc.h +++ b/src/pbaas/crosschainrpc.h @@ -29,6 +29,9 @@ static const uint32_t PBAAS_VERSION = 1; static const uint32_t PBAAS_VERSION_INVALID = 0; extern uint32_t PBAAS_TESTFORK_TIME; +extern const uint32_t PBAAS_PREMAINNET_ACTIVATION; + +extern std::string PBAAS_TEST_ETH_CONTRACT; class CTransaction; class CScript; @@ -1667,6 +1670,7 @@ class CNativeHashWriter } int GetType() const { return SER_GETHASH; } + CCurrencyDefinition::EHashTypes GetHashType() const { return nativeHashType; } int GetVersion() const { return PROTOCOL_VERSION; } template diff --git a/src/pbaas/identity.cpp b/src/pbaas/identity.cpp index e3c2a5252e8..d6a29923fc2 100644 --- a/src/pbaas/identity.cpp +++ b/src/pbaas/identity.cpp @@ -905,7 +905,7 @@ bool HasReferralRequired(const CIdentity &identity, const CTransaction &tx, int3 if (checkIdentity.IsValidUnrevoked() && (checkIdentity.parent == parentID || idID == parentID || - (chainActive[height]->nTime >= PBAAS_TESTFORK_TIME && !checkReferralID.IsNull() && idID == checkReferralID))) + (chainActive[height]->nTime >= PBAAS_PREMAINNET_ACTIVATION && !checkReferralID.IsNull() && idID == checkReferralID))) { checkIdentities.push_back(checkIdentity); } @@ -1240,7 +1240,7 @@ bool ValidateSpendingIdentityReservation(const CTransaction &tx, int32_t outNum, } newIdentity = CIdentity(p.vData[0]); if (newIdentity.parent.IsNull() && - chainActive[height - 1]->nTime > PBAAS_TESTFORK_TIME && + chainActive[height - 1]->nTime > PBAAS_PREMAINNET_ACTIVATION && !HasReferralRequired(newIdentity, tx, outNum, state, height, ConnectedChains.ThisChain())) { return state.Error("Cannot make identity without valid referral"); @@ -1878,7 +1878,7 @@ bool PrecheckIdentityReservation(const CTransaction &tx, int32_t outNum, CValida newIdentity = CIdentity(p.vData[0]); uint160 dummyParent; valid = newIdentity.IsValid() && - (((chainActive.LastTip()->nTime < PBAAS_TESTFORK_TIME || + (((chainActive.LastTip()->nTime < PBAAS_PREMAINNET_ACTIVATION || burnSet.count(newIdentity.GetID())) && newIdentity.name == CleanName(newIdentity.name, dummyParent)) || newIdentity.name == CleanName(newIdentity.name, dummyParent, true)) && diff --git a/src/pbaas/notarization.cpp b/src/pbaas/notarization.cpp index 629517fd6db..22094d246c5 100644 --- a/src/pbaas/notarization.cpp +++ b/src/pbaas/notarization.cpp @@ -166,7 +166,7 @@ CNotaryEvidence &CNotaryEvidence::MergeEvidence(const CNotaryEvidence &mergeWith return *this; } -CIdentitySignature::ESignatureVerification CNotaryEvidence::SignConfirmed(const std::set ¬arySet, int minConfirming, const CKeyStore &keyStore, const CTransaction &txToConfirm, const CIdentityID &signWithID, uint32_t height, CCurrencyDefinition::EHashTypes hashType) +CIdentitySignature::ESignatureVerification CNotaryEvidence::SignConfirmed(const std::set ¬arySet, int minConfirming, const CKeyStore &keyStore, const CTransaction &txToConfirm, const CIdentityID &signWithID, uint32_t height, CCurrencyDefinition::EHashTypes hashType, CNotaryEvidence *pNewSignatureEvidence) { if (!notarySet.count(signWithID)) { @@ -201,18 +201,21 @@ CIdentitySignature::ESignatureVerification CNotaryEvidence::SignConfirmed(const } uint32_t decisionHeight; - std::map confirmedAtHeight; - std::map rejectedAtHeight; + std::map notarySetRejects; + std::map notarySetConfirms; CNotaryEvidence::EStates sigCheckResult = CheckSignatureConfirmation(objHash, + hashType, notarySet, minConfirming, height, &decisionHeight, - &confirmedAtHeight, - &rejectedAtHeight); + ¬arySetRejects, + ¬arySetConfirms); - if (sigCheckResult == CNotaryEvidence::EStates::STATE_CONFIRMED || sigCheckResult == CNotaryEvidence::EStates::STATE_REJECTED) + if (notarySetConfirms.count(signWithID) || + sigCheckResult == CNotaryEvidence::EStates::STATE_CONFIRMED || + sigCheckResult == CNotaryEvidence::EStates::STATE_REJECTED) { return CIdentitySignature::ESignatureVerification::SIGNATURE_COMPLETE; } @@ -221,27 +224,17 @@ CIdentitySignature::ESignatureVerification CNotaryEvidence::SignConfirmed(const return CIdentitySignature::ESignatureVerification::SIGNATURE_INVALID; } - // sign for anything we can that is not already in the confirmedAtHeight signature block if decisionHeight is our height - int currentNumSigs = (decisionHeight == height && confirmedAtHeight.count(signWithID)) ? confirmedAtHeight[signWithID].signatures.size() : 0; - // all signatures present for this ID are correct, so we recover all pub keys and IDs, // then see if we have any of the keys in our wallet that are in the ID and have not already // signed CIdentity sigIdentity = CIdentity::LookupIdentity(signWithID, height); if (!sigIdentity.IsValid()) { - LogPrint("notarization", "%s: failed lookup of identity for rejection: %s\n", __func__, EncodeDestination(signWithID).c_str()); + LogPrint("notarization", "%s: failed lookup of identity for confirmation: %s\n", __func__, EncodeDestination(signWithID).c_str()); return CIdentitySignature::ESignatureVerification::SIGNATURE_INVALID; } else { - int sigsNeeded = std::max(sigIdentity.minSigs - currentNumSigs, 0); - if (!sigsNeeded) - { - LogPrint("notarization", "%s: Already signed with ID\n", __func__); - return CIdentitySignature::SIGNATURE_COMPLETE; - } - CIdentitySignature idSignature(height, std::set>(), hashType); uint256 signatureHash = idSignature.IdentitySignatureHash(std::vector({NotaryConfirmedKey()}), @@ -267,30 +260,46 @@ CIdentitySignature::ESignatureVerification CNotaryEvidence::SignConfirmed(const } } + // sign for anything we can that is not already in the confirmed set + int currentNumSigs = notarySetConfirms[signWithID].signatures.size(); + if (currentNumSigs) { - for (auto &oneSig : confirmedAtHeight[signWithID].signatures) + for (auto &oneSig : notarySetConfirms[signWithID].signatures) { CPubKey checkKey; - checkKey.RecoverCompact(signatureHash, oneSig); - validKeys.erase(checkKey.GetID()); + if (checkKey.RecoverCompact(signatureHash, oneSig)) + { + validKeys.erase(checkKey.GetID()); + } } } + int sigsToMake = std::max(sigIdentity.minSigs - currentNumSigs, 0); + // as long as we can continue to make new signatures, we do - for (auto keyIT = validKeys.begin(); sigsNeeded > 0 && keyIT != validKeys.end(); keyIT++) + for (auto keyIT = validKeys.begin(); sigsToMake > 0 && keyIT != validKeys.end(); keyIT++) { CKey signingKey; std::vector newSig; if (keyStore.GetKey(GetDestinationID(*keyIT), signingKey) && signingKey.SignCompact(signatureHash, newSig)) { idSignature.signatures.insert(newSig); - sigsNeeded--; + sigsToMake--; } } if (idSignature.signatures.size()) { + if (pNewSignatureEvidence) + { + std::map newSigMap; + newSigMap.insert(std::make_pair(signWithID, idSignature)); + CNotarySignature newSignature(systemID, output, true, newSigMap); + CCrossChainProof sigProof; + sigProof << newSignature; + pNewSignatureEvidence->MergeEvidence(CNotaryEvidence(systemID, output, STATE_CONFIRMING, sigProof)); + } AddToSignatures(notarySet, signWithID, idSignature, STATE_CONFIRMING); } @@ -309,7 +318,7 @@ CIdentitySignature::ESignatureVerification CNotaryEvidence::SignConfirmed(const return sigResult; } // if we have enough signatures for the ID, make sure we return complete - if (sigsNeeded == 0) + if (idSignature.signatures.size() >= sigIdentity.minSigs) { return CIdentitySignature::ESignatureVerification::SIGNATURE_COMPLETE; } @@ -323,7 +332,8 @@ CIdentitySignature::ESignatureVerification CNotaryEvidence::SignRejected(const s const CTransaction &txToConfirm, const CIdentityID &signWithID, uint32_t height, - CCurrencyDefinition::EHashTypes hashType) + CCurrencyDefinition::EHashTypes hashType, + CNotaryEvidence *pNewSignatureEvidence) { if (!notarySet.count(signWithID)) { @@ -364,6 +374,7 @@ CIdentitySignature::ESignatureVerification CNotaryEvidence::SignRejected(const s uint32_t decisionHeight; CNotaryEvidence::EStates sigCheckResult = CheckSignatureConfirmation(objHash, + hashType, notarySet, minConfirming, height, @@ -381,9 +392,6 @@ CIdentitySignature::ESignatureVerification CNotaryEvidence::SignRejected(const s return CIdentitySignature::ESignatureVerification::SIGNATURE_INVALID; } - // sign for anything we can that is not already in the rejectedAtHeight signature block if decisionHeight is our height - int currentNumSigs = (decisionHeight == height && rejectedAtHeight.count(signWithID)) ? rejectedAtHeight[signWithID].signatures.size() : 0; - // all signatures present for this ID are correct, so we recover all pub keys and IDs, // then see if we have any of the keys in our wallet that are in the ID and have not already // signed @@ -395,13 +403,6 @@ CIdentitySignature::ESignatureVerification CNotaryEvidence::SignRejected(const s } else { - int sigsNeeded = std::max(sigIdentity.minSigs - currentNumSigs, 0); - if (!sigsNeeded) - { - LogPrint("notarization", "%s: Already signed with ID\n", __func__); - return CIdentitySignature::SIGNATURE_COMPLETE; - } - CIdentitySignature idSignature(height, std::set>(), hashType); uint256 signatureHash = idSignature.IdentitySignatureHash(std::vector({NotaryRejectedKey()}), @@ -427,16 +428,28 @@ CIdentitySignature::ESignatureVerification CNotaryEvidence::SignRejected(const s } } + // sign for anything we can that is not already in the signature block + int currentNumSigs = rejectedAtHeight[signWithID].signatures.size(); + if (currentNumSigs) { for (auto &oneSig : rejectedAtHeight[signWithID].signatures) { CPubKey checkKey; - checkKey.RecoverCompact(signatureHash, oneSig); - validKeys.erase(checkKey.GetID()); + if (checkKey.RecoverCompact(signatureHash, oneSig)) + { + validKeys.erase(checkKey.GetID()); + } } } + int sigsNeeded = std::max(sigIdentity.minSigs - currentNumSigs, 0); + if (!sigsNeeded) + { + LogPrint("notarization", "%s: Already signed with ID\n", __func__); + return CIdentitySignature::SIGNATURE_COMPLETE; + } + // as long as we can continue to make new signatures, we do for (auto keyIT = validKeys.begin(); sigsNeeded > 0 && keyIT != validKeys.end(); keyIT++) { @@ -451,6 +464,15 @@ CIdentitySignature::ESignatureVerification CNotaryEvidence::SignRejected(const s if (idSignature.signatures.size()) { + if (pNewSignatureEvidence) + { + std::map newSigMap; + newSigMap.insert(std::make_pair(signWithID, idSignature)); + CNotarySignature newSignature(systemID, output, true, newSigMap); + CCrossChainProof sigProof; + sigProof << newSignature; + pNewSignatureEvidence->MergeEvidence(CNotaryEvidence(systemID, output, STATE_REJECTING, sigProof)); + } AddToSignatures(notarySet, signWithID, idSignature, STATE_REJECTING); } @@ -478,23 +500,29 @@ CIdentitySignature::ESignatureVerification CNotaryEvidence::SignRejected(const s } CNotaryEvidence::EStates CNotaryEvidence::CheckSignatureConfirmation(const uint256 &objHash, + CCurrencyDefinition::EHashTypes hashType, const std::set ¬arySet, int minConfirming, uint32_t checkHeight, uint32_t *pDecisionHeight, - std::map *pConfirmedAtHeight, - std::map *pRejectedAtHeight, - std::map>> *pNotarySetRejects, - std::map>> *pNotarySetConfirms) const + std::map *pNotarySetRejects, + std::map *pNotarySetConfirms, + std::map> *pRejectsByHeight, + std::map> *pConfirmsByHeight) const { - std::map> confirmedByHeight; - std::map> rejectedByHeight; + std::map> _rejectedByHeight; + std::map> _confirmedByHeight; + std::map> &rejectedByHeight = pRejectsByHeight ? *pRejectsByHeight : _rejectedByHeight; + std::map> &confirmedByHeight = pConfirmsByHeight ? *pConfirmsByHeight : _confirmedByHeight; + std::vector notarySignatures = GetNotarySignatures(&confirmedByHeight, &rejectedByHeight); - std::map>> _notarySetRejects; - std::map>> _notarySetConfirms; - std::map>> ¬arySetRejects = pNotarySetRejects ? *pNotarySetRejects : _notarySetRejects; - std::map>> ¬arySetConfirms = pNotarySetConfirms ? *pNotarySetConfirms : _notarySetConfirms; + std::map _notarySetRejects; + std::map _notarySetConfirms; + std::map ¬arySetRejects = pNotarySetRejects ? *pNotarySetRejects : _notarySetRejects; + std::map ¬arySetConfirms = pNotarySetConfirms ? *pNotarySetConfirms : _notarySetConfirms; + std::map partialConfirms; + std::map partialRejects; CNotaryEvidence::EStates retVal = CNotaryEvidence::EStates::STATE_CONFIRMING; @@ -509,16 +537,6 @@ CNotaryEvidence::EStates CNotaryEvidence::CheckSignatureConfirmation(const uint2 // write the object to the hash writer without a vector length prefix uint256 outputUTXOHash; { - CCurrencyDefinition::EHashTypes hashType = CCurrencyDefinition::EHashTypes::HASH_BLAKE2BMMR; - for (auto &oneSig : notarySignatures) - { - if (oneSig.signatures.size()) - { - hashType = (CCurrencyDefinition::EHashTypes)oneSig.signatures.begin()->second.hashType; - break; - } - } - CNativeHashWriter hw(hashType); hw << output; outputUTXOHash = hw.GetHash(); @@ -539,7 +557,7 @@ CNotaryEvidence::EStates CNotaryEvidence::CheckSignatureConfirmation(const uint2 // if this was signed on this chain, it isn't valid if signed after the check height if (oneSigBlock.systemID == ASSETCHAINS_CHAINID && height > checkHeight) { - break; + continue; } if (height != lastHeight) @@ -555,7 +573,7 @@ CNotaryEvidence::EStates CNotaryEvidence::CheckSignatureConfirmation(const uint2 if (!notarySet.count(oneIDSig.first)) { LogPrint("notarization", "%s: unauthorized notary identity: %s\n", __func__, EncodeDestination(oneIDSig.first).c_str()); - return EStates::STATE_INVALID; + continue; } CIdentity sigIdentity; @@ -604,14 +622,35 @@ CNotaryEvidence::EStates CNotaryEvidence::CheckSignatureConfirmation(const uint2 if (result == oneIDSig.second.SIGNATURE_INVALID) { LogPrint("notarization", "%s: invalid notary signature: %s\n", __func__, EncodeDestination(oneIDSig.first).c_str()); + continue; } + if (result == oneIDSig.second.SIGNATURE_COMPLETE) + { + partialConfirms.insert(oneIDSig); + } + else if (result == oneIDSig.second.SIGNATURE_PARTIAL) + { + auto it = partialConfirms.find(oneIDSig.first); + if (it != partialConfirms.end() && + it->second.blockHeight == oneIDSig.second.blockHeight) + { + for (auto &oneSig : oneIDSig.second.signatures) + { + it->second.AddSignature(oneSig); + } + } + else + { + partialConfirms.insert(oneIDSig); + } + } if (result != oneIDSig.second.SIGNATURE_COMPLETE) { // could store and return partial signatures here continue; } - notarySetConfirms[oneIDSig.first] = oneIDSig.second.signatures; + notarySetConfirms.insert(oneIDSig); } } else @@ -653,18 +692,39 @@ CNotaryEvidence::EStates CNotaryEvidence::CheckSignatureConfirmation(const uint2 rejectedByHeight[oneIDSig.second.blockHeight][oneIDSig.first].signatures.erase(oneDup); } - /* if (result == oneIDSig.second.SIGNATURE_INVALID) + if (result == oneIDSig.second.SIGNATURE_INVALID) { LogPrint("notarization", "%s: invalid notary signature for rejection: %s\n", __func__, EncodeDestination(oneIDSig.first).c_str()); - return EStates::STATE_INVALID; + continue; + } + + if (result == oneIDSig.second.SIGNATURE_COMPLETE) + { + partialRejects.insert(oneIDSig); } - else */ + else if (result == oneIDSig.second.SIGNATURE_PARTIAL) + { + auto it = partialRejects.find(oneIDSig.first); + if (it != partialRejects.end() && + it->second.blockHeight == oneIDSig.second.blockHeight) + { + for (auto &oneSig : oneIDSig.second.signatures) + { + it->second.AddSignature(oneSig); + } + } + else + { + partialRejects.insert(oneIDSig); + } + } + if (result != oneIDSig.second.SIGNATURE_COMPLETE) { - // could store and return partial signatures here + // partial signatures are in the by height maps continue; } - notarySetRejects.insert(std::make_pair(oneIDSig.first, oneIDSig.second.signatures)); + notarySetRejects.insert(oneIDSig); } } @@ -698,17 +758,11 @@ CNotaryEvidence::EStates CNotaryEvidence::CheckSignatureConfirmation(const uint2 { *pDecisionHeight = lastHeight; } - if (pConfirmedAtHeight && lastHeight && confirmedByHeight.count(lastHeight)) - { - *pConfirmedAtHeight = confirmedByHeight[lastHeight]; - } - if (pRejectedAtHeight && lastHeight && rejectedByHeight.count(lastHeight)) - { - *pRejectedAtHeight = rejectedByHeight[lastHeight]; - } if (retVal != EStates::STATE_CONFIRMED && retVal != EStates::STATE_REJECTED) { + notarySetConfirms = partialConfirms; + notarySetRejects = partialRejects; if (lastHeight == checkHeight && notarySetConfirms.size() >= notarySetRejects.size()) { @@ -1987,94 +2041,35 @@ bool CChainNotarizationData::CorrelatedFinalizationSpends(const std::vector associatedSpends; - associatedSpends.push_back(onePending.second); // this is a finalization or earned notarization, so it is first associated spend + associatedSpends.push_back(onePending.second); // this is a finalization or earned notarization, so it is an associated spend if (notarizationOutputMap.count(pendingNotarizationOutput)) { associatedIdx = notarizationOutputMap[pendingNotarizationOutput]; } - // if we are asssociated with a known node, + // if we are associated with a known notarization, // get additional associated evidence as well if (associatedIdx != -1) { // if there is a finalization, we need to add it and its evidence, - if (existingFinalization.IsValid() && existingFinalization.evidenceOutputs.size()) + if (existingFinalization.IsValid() && pEvidenceVec) { - CTransaction finalizationTx; - const CTransaction *pEvidenceOutputTx = nullptr; - if (existingFinalization.output.IsOnSameTransaction()) - { - pEvidenceOutputTx = &(txes[associatedIdx].first); - } - else + CTransaction finalizeTx; + uint256 finalizeBlockHash; + if (!myGetTransaction(onePending.second.txIn.prevout.hash, finalizeTx, finalizeBlockHash)) { - LOCK(mempool.cs); - uint256 hashBlock; - if (myGetTransaction(onePending.second.txIn.prevout.hash, finalizationTx, hashBlock)) - { - pEvidenceOutputTx = &finalizationTx; - } - else - { - LogPrint("notarization", "%s: cannot access transaction required as input for notarization\n", __func__); - return false; - } - } - - // add evidence outs as spends to close this entry as well - int afterMultiPart = existingFinalization.evidenceOutputs.size() ? existingFinalization.evidenceOutputs[0] : 0; - for (auto oneEvidenceOutN : existingFinalization.evidenceOutputs) - { - if (pEvidenceOutputTx->vout.size() <= oneEvidenceOutN) - { - LogPrint("notarization", "%s: indexing error for notarization evidence\n", __func__); - return false; - } - - if (pEvidenceVec && - oneEvidenceOutN >= afterMultiPart && - associatedIdx != -1) - { - CNotaryEvidence oneEvidenceObj(*pEvidenceOutputTx, oneEvidenceOutN, afterMultiPart); - if (oneEvidenceObj.IsValid()) - { - (*pEvidenceVec)[associatedIdx].push_back(oneEvidenceObj); - } - } - - associatedSpends.push_back( - CInputDescriptor(pEvidenceOutputTx->vout[oneEvidenceOutN].scriptPubKey, - pEvidenceOutputTx->vout[oneEvidenceOutN].nValue, - CTxIn(pEvidenceOutputTx->GetHash(), oneEvidenceOutN))); - evidenceOutputSet.insert(associatedSpends.back().txIn.prevout); + LogPrintf("%s: LIKELY LOCAL STATE OR INDEX CORRUPTION. Bootstrap, reindex, or resync recommended\n", __func__); + continue; } - } - - // unspent evidence is specific to the target notarization - std::vector> unspentEvidence = - CObjectFinalization::GetUnspentEvidence(currencyID, vtx[associatedIdx].first.hash, vtx[associatedIdx].first.n); - for (auto &oneEvidenceSpend : unspentEvidence) - { - // if not already added to our spends as evidence, add it - if (!evidenceOutputSet.count(oneEvidenceSpend.second.txIn.prevout)) + CValidationState state; + std::vector> finalizationEvidence = + existingFinalization.GetFinalizationEvidence(finalizeTx, onePending.second.txIn.prevout.n, state); + for (auto oneEvidenceItem : finalizationEvidence) { - COptCCParams tP; - CNotaryEvidence oneEvidenceObj; - if (pEvidenceVec && - oneEvidenceSpend.second.scriptPubKey.IsPayToCryptoCondition(tP) && - tP.IsValid() && - tP.vData.size() && - tP.evalCode == EVAL_NOTARY_EVIDENCE && - (oneEvidenceObj = CNotaryEvidence(tP.vData[0])).IsValid()) - { - (*pEvidenceVec)[associatedIdx].push_back(oneEvidenceObj); - } - - associatedSpends.push_back(oneEvidenceSpend.second); + (*pEvidenceVec)[associatedIdx].push_back(oneEvidenceItem.second); } } - spendsToClose[associatedIdx].insert(spendsToClose[associatedIdx].end(), associatedSpends.begin(), associatedSpends.end()); } else @@ -2166,157 +2161,17 @@ bool CChainNotarizationData::CorrelatedFinalizationSpends(const std::vectorvout.size() <= oneEvidenceOutN) - { - LogPrint("notarization", "%s: indexing error for notarization evidence\n", __func__); - return false; - } - - if (pEvidenceVec && - oneEvidenceOutN >= afterMultiPart && - associatedIdx != -1) - { - CNotaryEvidence oneEvidenceObj(*pEvidenceOutputTx, oneEvidenceOutN, afterMultiPart); - if (oneEvidenceObj.IsValid()) - { - (*pEvidenceVec)[associatedIdx].push_back(oneEvidenceObj); - } - } - - associatedSpends.push_back( - CInputDescriptor(pEvidenceOutputTx->vout[oneEvidenceOutN].scriptPubKey, - pEvidenceOutputTx->vout[oneEvidenceOutN].nValue, - CTxIn(pEvidenceOutputTx->GetHash(), oneEvidenceOutN))); - evidenceOutputSet.insert(associatedSpends.back().txIn.prevout); - } - - if (pEvidenceVec && - associatedIdx != -1) - { - // on a confirmed notarization, get input evidence as well, even though it doesn't need to be cleaned up - CTransaction priorOutputTx; - CNotaryEvidence oneEvidenceObj; - std::vector evidenceInVec; - for (auto oneIn : confirmedFinalization.evidenceInputs) - { - uint256 hashBlock; - if (priorOutputTx.GetHash() != pEvidenceOutputTx->vin[oneIn].prevout.hash && - !myGetTransaction(pEvidenceOutputTx->vin[oneIn].prevout.hash, priorOutputTx, hashBlock)) - { - printf("%s: cannot access transaction for notarization evidence\n", __func__); - LogPrint("notarization", "%s: cannot access transaction for notarization evidence\n", __func__); - return false; - } - - COptCCParams inP; - if (priorOutputTx.vout[pEvidenceOutputTx->vin[oneIn].prevout.n].scriptPubKey.IsPayToCryptoCondition(inP) && - inP.IsValid() && - inP.evalCode == EVAL_NOTARY_EVIDENCE && - inP.vData.size() && - (oneEvidenceObj = CNotaryEvidence(inP.vData[0])).IsValid() && - oneEvidenceObj.evidence.chainObjects.size()) - { - // there is no spend to store, as it has already been spent, but we - // still want to get its evidence - - // if we are starting a new object, finish the old one - if (!oneEvidenceObj.IsMultipartProof() || - ((CChainObject *)oneEvidenceObj.evidence.chainObjects[0])->object.md.index == 0) - { - // if we have a composite evidence object, store it and clear the vector - if (evidenceInVec.size()) - { - CNotaryEvidence multiPartEvidence(evidenceInVec); - if (multiPartEvidence.IsValid()) - { - (*pEvidenceVec)[associatedIdx].push_back(multiPartEvidence); - } - evidenceInVec.clear(); - } - } - - if (!oneEvidenceObj.IsMultipartProof()) - { - (*pEvidenceVec)[associatedIdx].push_back(oneEvidenceObj); - } - else - { - evidenceInVec.push_back(oneEvidenceObj); - } - } - } - - // if we have a composite evidence object, store it and clear the vector - if (evidenceInVec.size()) - { - CNotaryEvidence multiPartEvidence(evidenceInVec); - if (multiPartEvidence.IsValid()) - { - (*pEvidenceVec)[associatedIdx].push_back(multiPartEvidence); - } - else - { - printf("%s: invalid multipart evidence on input\n", __func__); - LogPrint("notarization", "%s: invalid multipart evidence on input\n", __func__); - return false; - } - } - } - } - - // unspent evidence is specific to the target notarization - std::vector> unspentEvidence = - CObjectFinalization::GetUnspentEvidence(currencyID, vtx[associatedIdx].first.hash, vtx[associatedIdx].first.n); - - for (auto &oneEvidenceSpend : unspentEvidence) + // if there is a finalization, we need to add it and its evidence, + if (confirmedFinalization.IsValid() && pEvidenceVec) { - // if not already added to our spends as evidence, add it - if (!evidenceOutputSet.count(oneEvidenceSpend.second.txIn.prevout)) + CValidationState state; + std::vector> finalizationEvidence = + confirmedFinalization.GetFinalizationEvidence(checkTx, oneConfirmed.second.txIn.prevout.n, state); + for (auto oneEvidenceItem : finalizationEvidence) { - COptCCParams tP; - CNotaryEvidence oneEvidenceObj; - int afterEvidence; - if (pEvidenceVec && - oneEvidenceSpend.second.scriptPubKey.IsPayToCryptoCondition(tP) && - tP.IsValid() && - tP.vData.size() && - tP.evalCode == EVAL_NOTARY_EVIDENCE && - (oneEvidenceObj = CNotaryEvidence(tP.vData[0])).IsValid()) - { - (*pEvidenceVec)[associatedIdx].push_back(oneEvidenceObj); - } - - associatedSpends.push_back(oneEvidenceSpend.second); + (*pEvidenceVec)[associatedIdx].push_back(oneEvidenceItem.second); } } - spendsToClose[associatedIdx].insert(spendsToClose[associatedIdx].end(), associatedSpends.begin(), associatedSpends.end()); } else @@ -4692,7 +4547,11 @@ bool CPBaaSNotarization::CreateAcceptedNotarization(const CCurrencyDefinition &e std::vector notarizationVec = ::AsVector(earnedNotarization); uint256 objHash = hw.write((const char *)&(notarizationVec[0]), notarizationVec.size()).GetHash(); - CNotaryEvidence::EStates confirmationState = notaryEvidence.CheckSignatureConfirmation(objHash, notarySet, minimumNotariesConfirm, height); + uint32_t decisionHeight; + std::map notarySetRejects; + std::map notarySetConfirms; + + CNotaryEvidence::EStates confirmationState = notaryEvidence.CheckSignatureConfirmation(objHash, hw.GetHashType(), notarySet, minimumNotariesConfirm, height, &decisionHeight, ¬arySetRejects, ¬arySetConfirms); if (confirmationState != CNotaryEvidence::EStates::STATE_CONFIRMED && !additionalEvidenceRequired) { return state.Error(errorPrefix + "insufficient signature evidence to confirm notarization"); @@ -5555,7 +5414,6 @@ bool CPBaaSNotarization::CreateEarnedNotarization(const CRPCChainData &externalS // entropy hash, then submit it to this chain with our own notarization or independently // if we don't qualify to notarize. // - std::set validCrossChainIndexSet; std::set invalidCrossChainIndexSet; { @@ -6150,19 +6008,26 @@ bool CPBaaSNotarization::CreateEarnedNotarization(const CRPCChainData &externalS submitParams.push_back(resultUni); } } + else + { + LogPrint("notarization", "Error getting challenge evidence %s\n", submitParams.write(1,2).c_str()); + } } - LogPrint("notarization", "Submitting challenges %s\n", submitParams.write(1,2).c_str()); - UniValue challengeResult; - params = UniValue(UniValue::VARR); - params.push_back(submitParams); - try + if (submitParams.size()) { - challengeResult = find_value(RPCCallRoot("submitchallenges", params), "result"); - } catch (exception e) - { - challengeResult = NullUniValue; + LogPrint("notarization", "Submitting challenges %s\n", submitParams.write(1,2).c_str()); + UniValue challengeResult; + params = UniValue(UniValue::VARR); + params.push_back(submitParams); + try + { + challengeResult = find_value(RPCCallRoot("submitchallenges", params), "result"); + } catch (exception e) + { + challengeResult = NullUniValue; + } + LogPrint("notarization", "Response from challenges %s\n", challengeResult.write(1,2).c_str()); } - LogPrint("notarization", "Response from challenges %s\n", challengeResult.write(1,2).c_str()); } } } @@ -6231,6 +6096,13 @@ bool CPBaaSNotarization::CreateEarnedNotarization(const CRPCChainData &externalS CProofRoot latestProofRoot = lastConfirmedProofRoot.IsValid() ? lastConfirmedProofRoot : lastStableProofRoot; if (externalSystem.chainDefinition.IsPBaaSChain() && cnd.vtx[notaryIdx].second.proofRoots.count(SystemID)) { + notarizationEvidence = CNotaryEvidence(CNotaryEvidence::TYPE_NOTARY_EVIDENCE, + CNotaryEvidence::VERSION_CURRENT, + CNotaryEvidence::STATE_CONFIRMED, + SystemID); + + notarizationEvidence.output = cnd.vtx[notaryIdx].first; + auto lastConfirmedRootIt = cnd.vtx[cnd.lastConfirmed].second.proofRoots.find(SystemID); if (lastConfirmedRootIt != cnd.vtx[cnd.lastConfirmed].second.proofRoots.end() && latestProofRoot.rootHeight <= lastConfirmedRootIt->second.rootHeight) @@ -6238,18 +6110,12 @@ bool CPBaaSNotarization::CreateEarnedNotarization(const CRPCChainData &externalS // if there is no change in proof root, we need no evidence to support a new notarization latestProofRoot = lastConfirmedRootIt->second; } - else if (latestProofRoot.IsValid()) + if (latestProofRoot.IsValid()) { // if we have challenge roots, add them to our // request for proof along with challenges UniValue newProofRequest(UniValue::VOBJ); - notarizationEvidence = CNotaryEvidence(CNotaryEvidence::TYPE_NOTARY_EVIDENCE, - CNotaryEvidence::VERSION_CURRENT, - CNotaryEvidence::STATE_CONFIRMED, - SystemID); - notarizationEvidence.output = cnd.vtx[notaryIdx].first; - newProofRequest.pushKV("type", EncodeDestination(CIdentityID(CNotaryEvidence::PrimaryProofKey()))); newProofRequest.pushKV("priorroot", cnd.vtx[notaryIdx].second.proofRoots[SystemID].ToUniValue()); if (challengeRoots.size()) @@ -6358,7 +6224,7 @@ bool CPBaaSNotarization::CreateEarnedNotarization(const CRPCChainData &externalS CPBaaSNotarization::GetAdjustedNotarizationModulo(ConnectedChains.ThisChain().blockNotarizationModulo, (height + 1) - mapBlockIndex[txes[cnd.lastConfirmed].second]->GetHeight()) : ConnectedChains.ThisChain().blockNotarizationModulo; - + int blockPeriodNumber = (height + 1) / adjustedNotarizationModulo; int priorBlockPeriod = mapBlockIt->second->GetHeight() / adjustedNotarizationModulo; @@ -7452,6 +7318,10 @@ bool CPBaaSNotarization::ConfirmOrRejectNotarizations(CWallet *pWallet, // ensure that there is no loss of primacy during the confirmation range bool attemptAutoConfirm = false; int idx = confirmIfSigned; + + std::map oldConfirms; + std::map newConfirms; + while (idx > 0) { // these are deleted, mutated below, and recreated each iteration @@ -7471,11 +7341,6 @@ bool CPBaaSNotarization::ConfirmOrRejectNotarizations(CWallet *pWallet, std::vector> additionalEvidence; - std::set sigSet; - - std::set notarySetRejects; - std::map notarySetConfirms; - std::vector> evidenceVec; std::vector> spendsToClose; std::vector extraSpends; @@ -7553,13 +7418,13 @@ bool CPBaaSNotarization::ConfirmOrRejectNotarizations(CWallet *pWallet, } } - CNotaryEvidence ne(ASSETCHAINS_CHAINID, cnd.vtx[idx].first, CNotaryEvidence::STATE_CONFIRMING); + CNotaryEvidence mergedEvidence(ASSETCHAINS_CHAINID, cnd.vtx[idx].first, CNotaryEvidence::STATE_CONFIRMING); CCcontract_info CC; CCcontract_info *cp; std::vector dests; COptCCParams p; - if (!(txes[idx].first.vout[ne.output.n].scriptPubKey.IsPayToCryptoCondition(p) && + if (!(txes[idx].first.vout[mergedEvidence.output.n].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode != EVAL_NONE)) { @@ -7570,33 +7435,38 @@ bool CPBaaSNotarization::ConfirmOrRejectNotarizations(CWallet *pWallet, uint256 objHash = hw.write((const char *)&(p.vData[0][0]), p.vData[0].size()).GetHash(); // combine signatures - CNotaryEvidence mergedEvidence = ne; + CNotaryEvidence signatureEvidence = mergedEvidence; for (auto &oneEvidenceObj : evidenceVec[idx]) { for (auto &oneSig : oneEvidenceObj.GetNotarySignatures()) { - mergedEvidence.evidence << oneSig; + signatureEvidence.evidence << oneSig; } } // add additional evidence - CCrossChainProof challengeProof(CCrossChainProof::VERSION_CURRENT); + CCrossChainProof tipProof(CCrossChainProof::VERSION_CURRENT); std::vector evidenceOutputs; - challengeProof << CEvidenceData(CNotaryEvidence::NotarizationTipKey(), + tipProof << CEvidenceData(CNotaryEvidence::NotarizationTipKey(), ::AsVector(CPrimaryProofDescriptor(std::vector({prunedData.vtx[bestFork.back()].first})))); - mergedEvidence.evidence << challengeProof; + mergedEvidence.evidence << tipProof; uint32_t decisionHeight; - std::map confirmedAtHeight; - std::map rejectedAtHeight; - CNotaryEvidence::EStates confirmationResult = mergedEvidence.CheckSignatureConfirmation(objHash, - notarySet, - minimumNotariesConfirm, - signingHeight, - &decisionHeight, - &confirmedAtHeight, - &rejectedAtHeight); + std::map notarySetRejects; + std::map notarySetConfirms; + std::map newSigMap; + + CNotaryEvidence::EStates confirmationResult = signatureEvidence.CheckSignatureConfirmation(objHash, + hashType, + notarySet, + minimumNotariesConfirm, + signingHeight, + &decisionHeight, + ¬arySetRejects, + ¬arySetConfirms); + + oldConfirms = notarySetConfirms; // rejected is irreversible, so we are done if (confirmationResult == CNotaryEvidence::EStates::STATE_REJECTED) @@ -7610,49 +7480,72 @@ bool CPBaaSNotarization::ConfirmOrRejectNotarizations(CWallet *pWallet, { LOCK(cs_main); + int numConfirms = notarySetConfirms.size(); + // if we can still sign, do so and check confirmation if (myIDSet.size()) { - cp = CCinit(&CC, EVAL_NOTARY_EVIDENCE); - dests = std::vector({CPubKey(ParseHex(CC.CChexstr))}); - - auto currentSignatures = mergedEvidence.GetNotarySignatures(); - for (auto &oneSignature : currentSignatures) - { - if (oneSignature.IsConfirmed()) - { - for (auto &oneIDSig : oneSignature.signatures) - { - std::set idDestinations; - - for (auto &oneKeySig : oneIDSig.second.signatures) - { - // TODO: POST HARDENING - for efficiency, not security, remove destinations already signed - } - } - } - } - + CIdentitySignature::ESignatureVerification signResult = CIdentitySignature::SIGNATURE_EMPTY; + CNotaryEvidence::EStates confirmationResult = CNotaryEvidence::EStates::STATE_REJECTING; { LOCK(pWallet->cs_wallet); // sign with all IDs under our control that are eligible for this currency for (auto &oneID : myIDSet) { - printf("Signing notarization (%s:%u) to confirm for %s\n", ne.output.hash.GetHex().c_str(), ne.output.n, EncodeDestination(oneID).c_str()); - LogPrint("notarization", "Signing notarization (%s:%u) to confirm for %s\n", ne.output.hash.GetHex().c_str(), ne.output.n, EncodeDestination(oneID).c_str()); + if (notarySetConfirms.count(oneID)) + { + // already signed, no need + continue; + } + printf("Signing notarization (%s:%u) to confirm for %s\n", mergedEvidence.output.hash.GetHex().c_str(), mergedEvidence.output.n, EncodeDestination(oneID).c_str()); + LogPrint("notarization", "Signing notarization (%s:%u) to confirm for %s\n", mergedEvidence.output.hash.GetHex().c_str(), mergedEvidence.output.n, EncodeDestination(oneID).c_str()); - auto signResult = ne.SignConfirmed(notarySet, minimumNotariesConfirm, *pWallet, txes[idx].first, oneID, signingHeight, hashType); + CNotaryEvidence newSigEvidence; + signResult = signatureEvidence.SignConfirmed(notarySet, minimumNotariesConfirm, *pWallet, txes[idx].first, oneID, signingHeight, hashType, &newSigEvidence); if (signResult == CIdentitySignature::SIGNATURE_PARTIAL || signResult == CIdentitySignature::SIGNATURE_COMPLETE) { - sigSet.insert(oneID); + if (newSigEvidence.evidence.chainObjects.size()) + { + auto notarySignatures = newSigEvidence.GetNotarySignatures(); + if (notarySignatures.size() && + notarySignatures[0].IsConfirmed() && + notarySignatures[0].signatures.size()) + { + for (auto &oneSig : notarySignatures[0].signatures) + { + auto it = newSigMap.find(oneSig.first); + if (it == newSigMap.end()) + { + newSigMap.insert(std::make_pair(oneID, oneSig.second)); + } + else + { + for (auto &oneRawSig : it->second.signatures) + { + it->second.AddSignature(oneRawSig); + } + } + } + } + } // if our signatures altogether have provided a complete validation, we can early out // check to see if this notarization now qualifies with signatures - if (signResult == CIdentitySignature::SIGNATURE_COMPLETE && - ne.CheckSignatureConfirmation(objHash, notarySet, minimumNotariesConfirm, signingHeight) == CNotaryEvidence::EStates::STATE_CONFIRMED) + if (signResult == CIdentitySignature::SIGNATURE_COMPLETE) { - break; + confirmationResult = signatureEvidence.CheckSignatureConfirmation(objHash, + hashType, + notarySet, + minimumNotariesConfirm, + signingHeight, + &decisionHeight, + ¬arySetRejects, + ¬arySetConfirms); + if (confirmationResult == CNotaryEvidence::EStates::STATE_CONFIRMED) + { + break; + } } } else @@ -7661,38 +7554,7 @@ bool CPBaaSNotarization::ConfirmOrRejectNotarizations(CWallet *pWallet, } } } - - if (!ne.evidence.Empty()) - { - mergedEvidence.MergeEvidence(ne, true, true); - - CScript evidenceScript = MakeMofNCCScript(CConditionObj(EVAL_NOTARY_EVIDENCE, dests, 1, &mergedEvidence)); - - // the value should be considered for reduction - if (evidenceScript.size() >= CScript::MAX_SCRIPT_ELEMENT_SIZE) - { - CNotaryEvidence emptyEvidence; - int baseOverhead = MakeMofNCCScript(CConditionObj(EVAL_NOTARY_EVIDENCE, dests, 1, &emptyEvidence)).size() + 128; - auto evidenceVec = mergedEvidence.BreakApart(CScript::MAX_SCRIPT_ELEMENT_SIZE - baseOverhead); - if (!evidenceVec.size()) - { - LogPrintf("%s: failed to package evidence for notarization of system %s\n", __func__, EncodeDestination(CIdentityID(cnd.vtx[0].second.currencyID)).c_str()); - return state.Error(errorPrefix + "failed to package evidence for notarization"); - } - for (auto &oneProof : evidenceVec) - { - evidenceOutputs.push_back(CTxOut(CNotaryEvidence::DEFAULT_OUTPUT_VALUE, MakeMofNCCScript(CConditionObj(EVAL_NOTARY_EVIDENCE, dests, 1, &oneProof)))); - } - } - else - { - evidenceOutputs.push_back(CTxOut(CNotaryEvidence::DEFAULT_OUTPUT_VALUE, evidenceScript)); - } - retVal = true; - } } - - confirmationResult = mergedEvidence.CheckSignatureConfirmation(objHash, notarySet, minimumNotariesConfirm, signingHeight); } else if (attemptAutoConfirm) { @@ -7711,6 +7573,39 @@ bool CPBaaSNotarization::ConfirmOrRejectNotarizations(CWallet *pWallet, continue; } + // if we've added any confirmations, add them to our evidence + if (newSigMap.size()) + { + // add new signatures needed + mergedEvidence.evidence << CNotarySignature(mergedEvidence.systemID, mergedEvidence.output, true, newSigMap); + + cp = CCinit(&CC, EVAL_NOTARY_EVIDENCE); + dests = std::vector({CPubKey(ParseHex(CC.CChexstr))}); + CScript evidenceScript = MakeMofNCCScript(CConditionObj(EVAL_NOTARY_EVIDENCE, dests, 1, &mergedEvidence)); + + // the value should be considered for reduction + if (evidenceScript.size() >= CScript::MAX_SCRIPT_ELEMENT_SIZE) + { + CNotaryEvidence emptyEvidence; + int baseOverhead = MakeMofNCCScript(CConditionObj(EVAL_NOTARY_EVIDENCE, dests, 1, &emptyEvidence)).size() + 128; + auto mergedVec = mergedEvidence.BreakApart(CScript::MAX_SCRIPT_ELEMENT_SIZE - baseOverhead); + if (!mergedVec.size()) + { + LogPrintf("%s: failed to package evidence for notarization of system %s\n", __func__, EncodeDestination(CIdentityID(cnd.vtx[0].second.currencyID)).c_str()); + return state.Error(errorPrefix + "failed to package evidence for notarization"); + } + for (auto &oneProof : mergedVec) + { + evidenceOutputs.push_back(CTxOut(CNotaryEvidence::DEFAULT_OUTPUT_VALUE, MakeMofNCCScript(CConditionObj(EVAL_NOTARY_EVIDENCE, dests, 1, &oneProof)))); + } + } + else + { + evidenceOutputs.push_back(CTxOut(CNotaryEvidence::DEFAULT_OUTPUT_VALUE, evidenceScript)); + } + retVal = true; + } + // if we have enough to finalize, do so as a combination of pre-existing evidence and this if (confirmationResult == CNotaryEvidence::EStates::STATE_CONFIRMED) { @@ -8099,9 +7994,6 @@ std::vector CPBaaSNotarization::SubmitFinalizedNotarizations(const CRPC CChainNotarizationData cnd; std::vector> notarizationTxes; - // define here to construct in netsted scope and then access below - CNotaryEvidence allEvidence(CNotaryEvidence::TYPE_NOTARY_EVIDENCE, CNotaryEvidence::VERSION_CURRENT); - uint32_t nHeight; int startingNotarizationIdx; int autoNotarizationIdx; @@ -8391,6 +8283,9 @@ std::vector CPBaaSNotarization::SubmitFinalizedNotarizations(const CRPC std::vector extraSpends; std::pair notarizationTxInfo; + // define here to construct in netsted scope and then access below + CNotaryEvidence allEvidence(CNotaryEvidence::TYPE_NOTARY_EVIDENCE, CNotaryEvidence::VERSION_CURRENT, CNotaryEvidence::STATE_CONFIRMED); + { LOCK2(cs_main, mempool.cs); @@ -8512,6 +8407,8 @@ std::vector CPBaaSNotarization::SubmitFinalizedNotarizations(const CRPC if (!earnedNotarizationIndexEntry.first.txhash.IsNull()) { allEvidence.output = cnd.vtx[cnd.lastConfirmed].first; + CNotaryEvidence signatureEvidence = allEvidence; + UniValue proofRequests(UniValue::VARR); UniValue proofRequest(UniValue::VOBJ); proofRequest.pushKV("type", EncodeDestination(CIdentityID(CNotaryEvidence::PrimaryProofKey()))); @@ -8579,7 +8476,29 @@ std::vector CPBaaSNotarization::SubmitFinalizedNotarizations(const CRPC auto notarySignatures = oneEvidenceObj.second.GetNotarySignatures(); for (auto &oneSig : notarySignatures) { - allEvidence.evidence << oneSig; + signatureEvidence.evidence << oneSig; + } + } + if (signatureEvidence.evidence.chainObjects.size()) + { + CCurrencyDefinition::EHashTypes hashType = (CCurrencyDefinition::EHashTypes)externalSystem.chainDefinition.proofProtocol; + CNativeHashWriter hw(hashType); + // we are earned notarizations, so it will not be mirrored and can be used as is + uint256 objHash = (hw << cnd.vtx[cnd.lastConfirmed].second).GetHash(); + + uint32_t decisionHeight; + std::map notaryRejects; + std::map notaryConfirms; + if (signatureEvidence.CheckSignatureConfirmation(objHash, + hashType, + pNotaryCurrency->GetNotarySet(), + pNotaryCurrency->MinimumNotariesConfirm(), + nHeight, + &decisionHeight, + ¬aryRejects, + ¬aryConfirms) == CNotaryEvidence::EStates::STATE_CONFIRMED) + { + allEvidence.evidence << CNotarySignature(allEvidence.systemID, allEvidence.output, true, notaryConfirms); } } } @@ -9495,7 +9414,7 @@ bool PreCheckAcceptedOrEarnedNotarization(const CTransaction &tx, int32_t outNum curDef.proofProtocol != curDef.PROOF_PBAASMMR; hw << currentNotarization; - if (evidence.CheckSignatureConfirmation(hw.GetHash(), curDef.GetNotarySet(), curDef.minNotariesConfirm, height - 1) != + if (evidence.CheckSignatureConfirmation(hw.GetHash(), hashType, curDef.GetNotarySet(), curDef.minNotariesConfirm, height - 1) != CNotaryEvidence::EStates::STATE_CONFIRMED && signatureRequired) { @@ -9619,6 +9538,10 @@ LRUCache> CObjectFinalization::GetFinalizationEvidence(const CTransaction &thisTx, int32_t outNum, CValidationState &state, CTransaction *pOutputTx) const { + if (thisTx.GetHash().IsNull()) + { + printf("%s: BAD PARAMETER\n", __func__); + } std::tuple>> cachedReturn; CUTXORef cacheKey = CUTXORef(thisTx.GetHash(), outNum); if (finalizationEvidenceCache.Get(cacheKey, cachedReturn)) @@ -9702,7 +9625,8 @@ CObjectFinalization::GetFinalizationEvidence(const CTransaction &thisTx, int32_t { auto oneIn = evidenceInputs[i]; uint256 hashBlock; - if (priorOutputTx.GetHash() != thisTx.vin[oneIn].prevout.hash && + if ((priorOutputTx.GetHash().IsNull() || + priorOutputTx.GetHash() != thisTx.vin[oneIn].prevout.hash) && !myGetTransaction(thisTx.vin[oneIn].prevout.hash, priorOutputTx, hashBlock)) { state.Error(std::string(__func__) + ": cannot access transaction " + thisTx.vin[oneIn].prevout.hash.GetHex() + " for notarization evidence"); @@ -10502,9 +10426,10 @@ bool PreCheckFinalizeNotarization(const CTransaction &tx, int32_t outNum, CValid } signatureState = oneEvidence.second.CheckSignatureConfirmation(objHash, - notarySet, - pNotaryCurrency->minNotariesConfirm, - height - 1); + hw.GetHashType(), + notarySet, + pNotaryCurrency->minNotariesConfirm, + height - 1); if (signatureState == CNotaryEvidence::STATE_CONFIRMED || signatureState == CNotaryEvidence::STATE_REJECTED) { break; @@ -10591,9 +10516,10 @@ bool PreCheckFinalizeNotarization(const CTransaction &tx, int32_t outNum, CValid } signatureState = oneEvidence.second.CheckSignatureConfirmation(objHash, - notarySet, - pNotaryCurrency->minNotariesConfirm, - height - 1); + hw.GetHashType(), + notarySet, + pNotaryCurrency->minNotariesConfirm, + height - 1); if (signatureState == CNotaryEvidence::STATE_CONFIRMED || signatureState == CNotaryEvidence::STATE_REJECTED) { break; @@ -10741,6 +10667,7 @@ bool PreCheckFinalizeNotarization(const CTransaction &tx, int32_t outNum, CValid // staked chain since the last notarization if (!evidenceMap[ASSETCHAINS_CHAINID].size() || evidenceMap[ASSETCHAINS_CHAINID][0].second.CheckSignatureConfirmation(objHash, + hw.GetHashType(), notarySet, pNotaryCurrency->minNotariesConfirm, height - 1) != CNotaryEvidence::STATE_REJECTED) @@ -10775,6 +10702,7 @@ bool PreCheckFinalizeNotarization(const CTransaction &tx, int32_t outNum, CValid } signatureState = oneEvidence.second.CheckSignatureConfirmation(objHash, + hw.GetHashType(), notarySet, pNotaryCurrency->minNotariesConfirm, height - 1); @@ -10868,7 +10796,7 @@ bool PreCheckFinalizeNotarization(const CTransaction &tx, int32_t outNum, CValid for (auto &oneItem : evidenceVec) { if (oneItem.second.GetNotarySignatures().size() && - oneItem.second.CheckSignatureConfirmation(objHash, notarySet, pNotaryCurrency->minNotariesConfirm, height - 1) != CNotaryEvidence::EStates::STATE_INVALID) + oneItem.second.CheckSignatureConfirmation(objHash, hw.GetHashType(), notarySet, pNotaryCurrency->minNotariesConfirm, height - 1) != CNotaryEvidence::EStates::STATE_INVALID) { hasSignature = true; break; @@ -11204,7 +11132,7 @@ bool CUTXORef::GetOutputTransaction(CTransaction &tx, uint256 &blockHash) const } // Sign the output object with an ID or signing authority of the ID from the wallet. -CNotaryEvidence CObjectFinalization::SignConfirmed(const std::set ¬arySet, int minConfirming, const CWallet *pWallet, const CTransaction &initialTx, const CIdentityID &signatureID, uint32_t signingHeight, CCurrencyDefinition::EHashTypes hashType) const +CNotaryEvidence CObjectFinalization::SignConfirmed(const std::set ¬arySet, int minConfirming, const CWallet *pWallet, const CTransaction &initialTx, const CIdentityID &signatureID, uint32_t signingHeight, CCurrencyDefinition::EHashTypes hashType, CNotaryEvidence *pNewSignatureEvidence) const { CNotaryEvidence retVal = CNotaryEvidence(ASSETCHAINS_CHAINID, output, CNotaryEvidence::STATE_CONFIRMING); @@ -11214,12 +11142,12 @@ CNotaryEvidence CObjectFinalization::SignConfirmed(const std::set ¬a uint256 blockHash; if (GetOutputTransaction(initialTx, tx, blockHash)) { - retVal.SignConfirmed(notarySet, minConfirming, *pWallet, tx, signatureID, signingHeight, hashType); + retVal.SignConfirmed(notarySet, minConfirming, *pWallet, tx, signatureID, signingHeight, hashType, pNewSignatureEvidence); } return retVal; } -CNotaryEvidence CObjectFinalization::SignRejected(const std::set ¬arySet, int minConfirming, const CWallet *pWallet, const CTransaction &initialTx, const CIdentityID &signatureID, uint32_t signingHeight, CCurrencyDefinition::EHashTypes hashType) const +CNotaryEvidence CObjectFinalization::SignRejected(const std::set ¬arySet, int minConfirming, const CWallet *pWallet, const CTransaction &initialTx, const CIdentityID &signatureID, uint32_t signingHeight, CCurrencyDefinition::EHashTypes hashType, CNotaryEvidence *pNewSignatureEvidence) const { CNotaryEvidence retVal = CNotaryEvidence(ASSETCHAINS_CHAINID, output, CNotaryEvidence::STATE_REJECTING); @@ -11229,7 +11157,7 @@ CNotaryEvidence CObjectFinalization::SignRejected(const std::set ¬ar uint256 blockHash; if (GetOutputTransaction(initialTx, tx, blockHash)) { - retVal.SignRejected(notarySet, minConfirming, *pWallet, tx, signatureID, signingHeight, hashType); + retVal.SignRejected(notarySet, minConfirming, *pWallet, tx, signatureID, signingHeight, hashType, pNewSignatureEvidence); } return retVal; } diff --git a/src/pbaas/notarization.h b/src/pbaas/notarization.h index 9ecfe28005d..8f5fdb421bd 100644 --- a/src/pbaas/notarization.h +++ b/src/pbaas/notarization.h @@ -246,8 +246,8 @@ class CObjectFinalization GetFinalizationEvidence(const CTransaction &thisTx, int32_t outputNum, CValidationState &state, CTransaction *pOutputTx=nullptr) const; // Sign the output object with an ID or signing authority of the ID from the wallet. - CNotaryEvidence SignConfirmed(const std::set ¬arySet, int minConfirming, const CWallet *pWallet, const CTransaction &initialTx, const CIdentityID &signatureID, uint32_t signingHeight, CCurrencyDefinition::EHashTypes hashType) const; - CNotaryEvidence SignRejected(const std::set ¬arySet, int minConfirming, const CWallet *pWallet, const CTransaction &initialTx, const CIdentityID &signatureID, uint32_t signingHeight, CCurrencyDefinition::EHashTypes hashType) const; + CNotaryEvidence SignConfirmed(const std::set ¬arySet, int minConfirming, const CWallet *pWallet, const CTransaction &initialTx, const CIdentityID &signatureID, uint32_t signingHeight, CCurrencyDefinition::EHashTypes hashType, CNotaryEvidence *pNewSignatureEvidence=nullptr) const; + CNotaryEvidence SignRejected(const std::set ¬arySet, int minConfirming, const CWallet *pWallet, const CTransaction &initialTx, const CIdentityID &signatureID, uint32_t signingHeight, CCurrencyDefinition::EHashTypes hashType, CNotaryEvidence *pNewSignatureEvidence=nullptr) const; static std::vector> GetUnspentConfirmedFinalizations(const uint160 ¤cyID); static std::vector> GetUnspentPendingFinalizations(const uint160 ¤cyID); diff --git a/src/pbaas/pbaas.cpp b/src/pbaas/pbaas.cpp index 792f70641e4..6df6f48beee 100644 --- a/src/pbaas/pbaas.cpp +++ b/src/pbaas/pbaas.cpp @@ -4222,16 +4222,21 @@ void CConnectedChains::CheckOracleUpgrades() upgradeData.resize(upgradeData.size() + 1); std::get<0>(*upgradeData.rbegin()) = ParseHex(oracleID.contentMap[TestForkUpgradeKey()].GetHex()); } - else if (oracleID.contentMap.count(PBaaSUpgradeKey())) + if (oracleID.contentMap.count(PBaaSUpgradeKey())) { upgradeData.resize(upgradeData.size() + 1); std::get<0>(*upgradeData.rbegin()) = ParseHex(oracleID.contentMap[PBaaSUpgradeKey()].GetHex()); } - else if (oracleID.contentMap.count(OptionalPBaaSUpgradeKey())) + if (oracleID.contentMap.count(OptionalPBaaSUpgradeKey())) { upgradeData.resize(upgradeData.size() + 1); std::get<0>(*upgradeData.rbegin()) = ParseHex(oracleID.contentMap[OptionalPBaaSUpgradeKey()].GetHex()); } + if (PBAAS_TESTMODE && oracleID.contentMap.count(TestnetEthContractUpgradeKey())) + { + LOCK(ConnectedChains.cs_mergemining); + activeUpgradesByKey.insert({TestnetEthContractUpgradeKey(), CUpgradeDescriptor(ParseHex(oracleID.contentMap[TestnetEthContractUpgradeKey()].GetHex()))}); + } CUpgradeDescriptor oneUpgrade; if (upgradeData.size()) @@ -4247,8 +4252,12 @@ void CConnectedChains::CheckOracleUpgrades() } } - auto upgradeTestForkIt = activeUpgradesByKey.find(TestForkUpgradeKey()); - auto upgradePBaaSIt = activeUpgradesByKey.find(PBaaSUpgradeKey()); + std::map::iterator upgradeTestNetEthContractIt = activeUpgradesByKey.find(TestnetEthContractUpgradeKey()); + std::map::iterator upgradeTestForkIt = activeUpgradesByKey.find(TestForkUpgradeKey()); + std::map::iterator upgradePBaaSIt = activeUpgradesByKey.find(PBaaSUpgradeKey()); + std::map::iterator stoppingIt = activeUpgradesByKey.end(); + + std::string gracefulStop; if (upgradeTestForkIt != activeUpgradesByKey.end() && upgradeTestForkIt->second.minDaemonVersion <= GetVerusVersion()) @@ -4263,14 +4272,41 @@ void CConnectedChains::CheckOracleUpgrades() } else { - printf("%s: ERROR - THE NETWORK IS UPGRADING TO PUBLIC BLOCKCHAINS AS A SERVICE PROTOCOL (PBAAS) 1.0 - UPGRADE TO VERSION %s TO SYNC PAST BLOCK %u ON THE VERUS PBAAS NETWORK\n", __func__, VersionString(upgradePBaaSIt->second.minDaemonVersion).c_str(), upgradePBaaSIt->second.upgradeBlockHeight - 1); - if (KOMODO_STOPAT == 0 || KOMODO_STOPAT > (upgradePBaaSIt->second.upgradeBlockHeight - 1)) + stoppingIt = upgradePBaaSIt; + gracefulStop = "PUBLIC BLOCKCHAINS AS A SERVICE PROTOCOL (PBAAS) 1.0"; + } + } + if (upgradeTestNetEthContractIt != activeUpgradesByKey.end()) + { + if (upgradeTestNetEthContractIt->second.minDaemonVersion <= GetVerusVersion()) + { + std::string oldVal = PBAAS_TEST_ETH_CONTRACT; + PBAAS_TEST_ETH_CONTRACT = CTransferDestination::EncodeEthDestination(upgradeTestNetEthContractIt->second.upgradeID); + if (oldVal != PBAAS_TEST_ETH_CONTRACT && + LogAcceptCategory("ethbridge")) + { + printf("Prior Ethereum bridge contract id was %s\n, upgraded to: %s\n", oldVal.c_str(), PBAAS_TEST_ETH_CONTRACT.c_str()); + LogPrintf("Prior Ethereum bridge contract id was %s\n, upgraded to: %s\n", oldVal.c_str(), PBAAS_TEST_ETH_CONTRACT.c_str()); + } + } + else + { + if (stoppingIt == activeUpgradesByKey.end() || stoppingIt->second.upgradeBlockHeight > upgradeTestNetEthContractIt->second.upgradeBlockHeight) { - LogPrintf("%s: ERROR - THE NETWORK IS UPGRADING TO PUBLIC BLOCKCHAINS AS A SERVICE PROTOCOL (PBAAS) 1.0 - UPGRADE TO VERSION %s TO SYNC PAST BLOCK %u ON THE VERUS PBAAS NETWORK\n", __func__, VersionString(upgradePBaaSIt->second.minDaemonVersion).c_str(), upgradePBaaSIt->second.upgradeBlockHeight - 1); - KOMODO_STOPAT = upgradePBaaSIt->second.upgradeBlockHeight - 1; + stoppingIt = upgradeTestNetEthContractIt; + gracefulStop = "UPGRADED TESTNET ETHEREUM BRIDGE CONTRACTS"; } } } + if (stoppingIt != activeUpgradesByKey.end()) + { + printf("%s: ERROR - THE NETWORK IS UPGRADING TO %s - UPGRADE TO VERSION %s TO SYNC PAST BLOCK %u ON THE VERUS PBAAS NETWORK\n", __func__, gracefulStop.c_str(), VersionString(stoppingIt->second.minDaemonVersion).c_str(), stoppingIt->second.upgradeBlockHeight - 1); + if (KOMODO_STOPAT == 0 || KOMODO_STOPAT > (upgradePBaaSIt->second.upgradeBlockHeight - 1)) + { + LogPrintf("%s: ERROR - THE NETWORK IS UPGRADING TO %s - UPGRADE TO VERSION %s TO SYNC PAST BLOCK %u ON THE VERUS PBAAS NETWORK\n", __func__, gracefulStop.c_str(), VersionString(stoppingIt->second.minDaemonVersion).c_str(), stoppingIt->second.upgradeBlockHeight - 1); + KOMODO_STOPAT = stoppingIt->second.upgradeBlockHeight - 1; + } + } } bool CConnectedChains::IsUpgradeActive(const uint160 &upgradeID, uint32_t blockHeight, uint32_t blockTime) const diff --git a/src/pbaas/pbaas.h b/src/pbaas/pbaas.h index 34753638e6c..292c0698dc2 100644 --- a/src/pbaas/pbaas.h +++ b/src/pbaas/pbaas.h @@ -1198,6 +1198,18 @@ class CConnectedChains return CCrossChainRPCData::GetConditionID(key, systemID); } + static std::string TestnetEthContractUpgradeKeyName() + { + return "vrsc::system.upgradedata.testnetethcontractupgrade"; + } + + static uint160 TestnetEthContractUpgradeKey() + { + static uint160 nameSpace; + static uint160 key = CVDXF_Data::GetDataKey(TestnetEthContractUpgradeKeyName(), nameSpace); + return key; + } + static std::string TestForkUpgradeKeyName() { return "vrsc::system.upgradedata.pbaastestfork"; diff --git a/src/pbaas/reserves.cpp b/src/pbaas/reserves.cpp index b2e961a6b99..e38aaf314b5 100644 --- a/src/pbaas/reserves.cpp +++ b/src/pbaas/reserves.cpp @@ -593,10 +593,8 @@ bool CCrossChainImport::GetImportInfo(const CTransaction &importTx, if (IsVerusActive() && !IsVerusMainnetActive()) { - // TODO: VNEXT retroactively hardcoded first testnet contract address, remove and ensure currency - // definition is correct for vETH gateway on next testnet importFromDef.nativeCurrencyID.SetAuxDest( - CTransferDestination(CTransferDestination::DEST_ETH, ::AsVector(CTransferDestination::DecodeEthDestination("0x3fa3a60240ef59460f5b34e2ec5a06ab892a2d00"))), + CTransferDestination(CTransferDestination::DEST_ETH, ::AsVector(CTransferDestination::DecodeEthDestination(PBAAS_TEST_ETH_CONTRACT))), 0); } else diff --git a/src/primitives/block.h b/src/primitives/block.h index bc311e81b15..ad6e21022b3 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -1111,9 +1111,6 @@ class CNotarySignature return signatureKey; } - CIdentitySignature::ESignatureVerification SignConfirmed(const std::set ¬arySet, int minConfirming, const CKeyStore &keyStore, const CTransaction &txToConfirm, const CIdentityID &signWithID, uint32_t height, CCurrencyDefinition::EHashTypes hashType); - CIdentitySignature::ESignatureVerification SignRejected(const std::set ¬arySet, int minConfirming, const CKeyStore &keyStore, const CTransaction &txToConfirm, const CIdentityID &signWithID, uint32_t height, CCurrencyDefinition::EHashTypes hashType); - bool IsConfirmed() const { return confirmed; @@ -2238,14 +2235,15 @@ class CNotaryEvidence } EStates CheckSignatureConfirmation(const uint256 &objHash, + CCurrencyDefinition::EHashTypes hashType, const std::set ¬arySet, int minConfirming, uint32_t checkHeight=0, uint32_t *pDecisionHeight=nullptr, - std::map *pConfirmedAtHeight=nullptr, - std::map *pRejectedAtHeight=nullptr, - std::map>> *pNotarySetRejects=nullptr, - std::map>> *pNotarySetConfirms=nullptr) const; + std::map *pNotarySetRejects=nullptr, + std::map *pNotarySetConfirms=nullptr, + std::map> *pRejectsByHeight=nullptr, + std::map> *pConfirmsByHeight=nullptr) const; static std::string NotarySignatureKeyName() { @@ -2365,8 +2363,8 @@ class CNotaryEvidence return proofKey; } - CIdentitySignature::ESignatureVerification SignConfirmed(const std::set ¬arySet, int minConfirming, const CKeyStore &keyStore, const CTransaction &txToConfirm, const CIdentityID &signWithID, uint32_t height, CCurrencyDefinition::EHashTypes hashType); - CIdentitySignature::ESignatureVerification SignRejected(const std::set ¬arySet, int minConfirming, const CKeyStore &keyStore, const CTransaction &txToConfirm, const CIdentityID &signWithID, uint32_t height, CCurrencyDefinition::EHashTypes hashType); + CIdentitySignature::ESignatureVerification SignConfirmed(const std::set ¬arySet, int minConfirming, const CKeyStore &keyStore, const CTransaction &txToConfirm, const CIdentityID &signWithID, uint32_t height, CCurrencyDefinition::EHashTypes hashType, CNotaryEvidence *pNewSignatures=nullptr); + CIdentitySignature::ESignatureVerification SignRejected(const std::set ¬arySet, int minConfirming, const CKeyStore &keyStore, const CTransaction &txToConfirm, const CIdentityID &signWithID, uint32_t height, CCurrencyDefinition::EHashTypes hashType, CNotaryEvidence *pNewSignatures=nullptr); bool IsMultipartProof() const { diff --git a/src/rpc/pbaasrpc.cpp b/src/rpc/pbaasrpc.cpp index 995a4addeb7..3b2c605cf8a 100644 --- a/src/rpc/pbaasrpc.cpp +++ b/src/rpc/pbaasrpc.cpp @@ -8990,7 +8990,7 @@ UniValue sendcurrency(const UniValue& params, bool fHelp) static std::set paramSet({"currency", "amount", "convertto", "exportto", "feecurrency", "via", "address", "exportid", "exportcurrency", "refundto", "memo", "preconvert", "burn", "burnweight", "mintnew", "opret"}); - printf("%s: params[1]: %s\n", __func__, params[1].write(1,2).c_str()); + //printf("%s: params[1]: %s\n", __func__, params[1].write(1,2).c_str()); try { diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 1bbc3673e1f..ca44d61a671 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -474,6 +474,7 @@ static const CRPCCommand vRPCCommands[] = { "wallet", "getaccountaddress", &getaccountaddress, true }, { "wallet", "getaccount", &getaccount, true }, { "wallet", "getaddressesbyaccount", &getaddressesbyaccount, true }, + { "wallet", "prunespentwallettransactions", &prunespentwallettransactions, false }, { "wallet", "getbalance", &getbalance, false }, { "wallet", "getbalance64", &getbalance64, false }, { "wallet", "getnewaddress", &getnewaddress, true }, diff --git a/src/rpc/server.h b/src/rpc/server.h index b6ab271dc90..468ebdc20d4 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -309,6 +309,7 @@ extern UniValue signmessage(const UniValue& params, bool fHelp); extern UniValue verifymessage(const UniValue& params, bool fHelp); extern UniValue getreceivedbyaddress(const UniValue& params, bool fHelp); extern UniValue getreceivedbyaccount(const UniValue& params, bool fHelp); +extern UniValue prunespentwallettransactions(const UniValue& params, bool fHelp); extern UniValue getbalance(const UniValue& params, bool fHelp); extern UniValue getbalance64(const UniValue& params, bool fHelp); extern UniValue getunconfirmedbalance(const UniValue& params, bool fHelp); diff --git a/src/version.h b/src/version.h index a837f129291..85831a51014 100644 --- a/src/version.h +++ b/src/version.h @@ -35,6 +35,6 @@ static const int MEMPOOL_GD_VERSION = 60002; static const int NO_BLOOM_VERSION = 170004; #define KOMODO_VERSION "0.2.1" -#define VERUS_VERSION "0.9.9-2" +#define VERUS_VERSION "0.9.9-3" #endif // BITCOIN_VERSION_H diff --git a/src/wallet-utility.cpp b/src/wallet-utility.cpp index f03c822ac5c..15a9ec4b4b2 100644 --- a/src/wallet-utility.cpp +++ b/src/wallet-utility.cpp @@ -18,6 +18,8 @@ uint160 ASSETCHAINS_CHAINID; uint160 VERUS_CHAINID; std::string VERUS_CHAINNAME; uint32_t PBAAS_TESTFORK_TIME; +const uint32_t PBAAS_PREMAINNET_ACTIVATION = 1679072400; +std::string PBAAS_TEST_ETH_CONTRACT = "0x3fa3a60240ef59460f5b34e2ec5a06ab892a2d00"; bool PARAMS_LOADED; int64_t MAX_MONEY = 200000000 * 100000000LL; int64_t MAX_SUPPLY = 50000000000LL * 100000000LL; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index f7103815fe9..f2ba745f414 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1902,6 +1902,110 @@ CAmount GetAccountBalance(const string& strAccount, int nMinDepth, const isminef return GetAccountBalance(walletdb, strAccount, nMinDepth, filter); } +UniValue prunespentwallettransactions(const UniValue& params, bool fHelp) +{ + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + + if (fHelp || params.size() > 1 ) + throw runtime_error( + "prunespentwallettransactions \"txid\"\n" + "\nRemove all txs that are spent. You can clear all txs bar one, by specifiying a txid.\n" + "\nPlease backup your wallet.dat before running this command.\n" + "\nArguments:\n" + "1. \"txid\" (string, optional) The transaction id to keep.\n" + "\nResult:\n" + "{\n" + " \"total_transactions\" : n, (numeric) Transactions in wallet of " + strprintf("%s",komodo_chainname()) + "\n" + " \"remaining_transactions\" : n, (numeric) Transactions in wallet after clean.\n" + " \"removed_transactions\" : n, (numeric) The number of transactions removed.\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("prunespentwallettransactions", "") + + HelpExampleCli("prunespentwallettransactions","\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") + + HelpExampleRpc("prunespentwallettransactions", "") + + HelpExampleRpc("prunespentwallettransactions","\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + UniValue ret(UniValue::VOBJ); + uint256 exception; int32_t txs = pwalletMain->mapWallet.size(); + std::vector TxToRemove; + if (params.size() == 1) + { + exception.SetHex(params[0].get_str()); + uint256 tmp_hash; CTransaction tmp_tx; + if (GetTransaction(exception,tmp_tx,tmp_hash,false)) + { + if ( !pwalletMain->IsMine(tmp_tx) ) + { + throw runtime_error("\nThe transaction is not yours!\n"); + } + else + { + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if ( wtx.GetHash() != exception ) + { + TxToRemove.push_back(wtx.GetHash()); + } + } + } + } + else + { + throw runtime_error("\nThe transaction could not be found!\n"); + } + } + else + { + // get all locked utxos to relock them later. + vector vLockedUTXO; + pwalletMain->ListLockedCoins(vLockedUTXO); + // unlock all coins so that the following call containes all utxos. + pwalletMain->UnlockAllCoins(); + // listunspent call... this gets us all the txids that are unspent, we search this list for the oldest tx, + vector vecOutputs; + assert(pwalletMain != NULL); + // include all coins, even immature + pwalletMain->AvailableCoins(vecOutputs, false, NULL, true, true, true, true, true, true); + int32_t oldestTxDepth = 0; + BOOST_FOREACH(const COutput& out, vecOutputs) + { + if ( out.nDepth > oldestTxDepth ) + oldestTxDepth = out.nDepth; + } + oldestTxDepth = oldestTxDepth + 1; // add extra block just for safety. + // lock all the previouly locked coins. + BOOST_FOREACH(COutPoint &outpt, vLockedUTXO) { + pwalletMain->LockCoin(outpt); + } + + // then add all txs in the wallet before this block to the list to remove. + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (wtx.GetDepthInMainChain() > oldestTxDepth) + TxToRemove.push_back(wtx.GetHash()); + } + } + + // erase txs + BOOST_FOREACH (uint256& hash, TxToRemove) + { + pwalletMain->EraseFromWallet(hash); + LogPrintf("Erased %s from wallet.\n",hash.ToString().c_str()); + } + + // build return JSON for stats. + int remaining = pwalletMain->mapWallet.size(); + ret.push_back(Pair("total_transactions", (int)txs)); + ret.push_back(Pair("remaining_transactions", (int)remaining)); + ret.push_back(Pair("removed_transactions", (int)(txs-remaining))); + return (ret); +} + UniValue getbalance(const UniValue& params, bool fHelp) { diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index e609a601425..2374a697b18 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1871,7 +1871,7 @@ bool CWallet::VerusSelectStakeOutput(CBlock *pBlock, arith_uint256 &hashResult, ((txout.tx->vout[txout.i].scriptPubKey.IsPayToCryptoCondition(p) && extendedStake && canSpend) || - (!p.IsValid() && (whichType == TX_PUBKEY || whichType == TX_PUBKEYHASH) && ::IsMine(*this, destinations[0])))) + (!p.IsValid() && (whichType == TX_PUBKEY || whichType == TX_PUBKEYHASH) && ::IsMine(*this, destinations[0]) == ISMINE_SPENDABLE))) { uint256 txHash = txout.tx->GetHash(); checkStakeTx.vin.push_back(CTxIn(COutPoint(txHash, txout.i))); @@ -1883,28 +1883,32 @@ bool CWallet::VerusSelectStakeOutput(CBlock *pBlock, arith_uint256 &hashResult, { bool isValid = true; // after PBaaS activation, - // we can't stake an output that is to an ID, which has been modified more recently than stakeage - if (whichType == txnouttype::TX_CRYPTOCONDITION && - isPBaaS && - (chainActive.Height() >= (nHeight - 1) ? - chainActive[nHeight - 1]->nTime > PBAAS_TESTFORK_TIME : - chainActive.LastTip()->nTime > PBAAS_TESTFORK_TIME)) + // we can't stake an output that has a destination to an ID, + // which has been modified more recently than stakeage + if (whichType == txnouttype::TX_CRYPTOCONDITION && isPBaaS) { for (auto &oneDest : destinations) { if (oneDest.which() == COptCCParams::ADDRTYPE_ID) { uint256 idTxId; - std::pair keyAndIdentity; - if (!GetIdentity(CIdentityID(GetDestinationID(oneDest)), keyAndIdentity) || - (nHeight - keyAndIdentity.first.blockHeight) < VERUS_MIN_STAKEAGE) + CIdentity destIdentity; + uint32_t idHeight; + if (!(destIdentity = CIdentity::LookupIdentity(GetDestinationID(oneDest), 0, &idHeight)).IsValid() || + (nHeight - idHeight) < VERUS_MIN_STAKEAGE) { - if (LogAcceptCategory("staking") && !keyAndIdentity.second.IsValid()) + if (LogAcceptCategory("staking")) { - LogPrintf("%s: Do not have ID %s in wallet\n", __func__, EncodeDestination(CIdentityID(GetDestinationID(oneDest))).c_str()); + if (!destIdentity.IsValid()) + { + LogPrintf("%s: Invalid ID %s as UTXO destination\n", __func__, EncodeDestination(CIdentityID(GetDestinationID(oneDest))).c_str()); + } + else + { + LogPrintf("%s: Identity that has been updated more recently than minimum stake age (%d) blocks renders UTXO ineligible to stake\n", __func__, EncodeDestination(CIdentityID(GetDestinationID(oneDest))).c_str(), VERUS_MIN_STAKEAGE); + } } isValid = false; - break; } } }