diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index bebd2a23dd1..e1c1578d379 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,7 +7,7 @@ stages: ######################################################################################################################## variables: - VERSION: 0.9.9-4 + VERSION: 0.9.9-5 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 f7ada08fddc..5ec1e6e6af4 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -## VerusCoin version 0.9.9-4 +## VerusCoin version 0.9.9-5 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 cc1a30708b8..e5cc878a802 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-4 +VerusCoin Command Line Tools v0.9.9-5 Contents: verusd - VerusCoin daemon diff --git a/doc/man/verus-cli/mac/README.txt b/doc/man/verus-cli/mac/README.txt index 380731284ac..e6da8190a06 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-4 +VerusCoin Command Line Tools v0.9.9-5 Contents: verusd - VerusCoin daemon. diff --git a/doc/man/verus-cli/windows/README.txt b/doc/man/verus-cli/windows/README.txt index 823bb121ce4..435b9627d73 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-4 +VerusCoin Command Line Tools v0.9.9-5 Contents: verusd.exe - VerusCoin daemon diff --git a/src/cc/eval.cpp b/src/cc/eval.cpp index 96200d0c566..86ddbd41765 100644 --- a/src/cc/eval.cpp +++ b/src/cc/eval.cpp @@ -125,7 +125,7 @@ bool Eval::Dispatch(const CC *cond, const CTransaction &txTo, unsigned int nIn, return(ProcessCC(cp,this, vparams, txTo, nIn, fulfilled)); break; } - return Invalid("invalid-code, dont forget to add EVAL_NEWCC to Eval::Dispatch"); + return Invalid("invalid smart transaction code"); } diff --git a/src/cryptoconditions/src/cryptoconditions.c b/src/cryptoconditions/src/cryptoconditions.c index e7a0a032aee..cb5bddfafcb 100644 --- a/src/cryptoconditions/src/cryptoconditions.c +++ b/src/cryptoconditions/src/cryptoconditions.c @@ -363,9 +363,11 @@ int cc_visit(CC *cond, CCVisitor visitor) { int cc_verify(const struct CC *cond, const unsigned char *msg, size_t msgLength, int doHashMsg, const unsigned char *condBin, size_t condBinLength, VerifyEval verifyEval, void *evalContext, int checkSig) { - unsigned char targetBinary[2000]; + + unsigned char targetBinary[MAX_BINARY_CC_SIZE]; + //fprintf(stderr,"in cc_verify cond.%p msg.%p[%d] dohash.%d condbin.%p[%d]\n",cond,msg,(int32_t)msgLength,doHashMsg,condBin,(int32_t)condBinLength); - const size_t binLength = cc_conditionBinary(cond, targetBinary, 2000); + const size_t binLength = cc_conditionBinary(cond, targetBinary, MAX_BINARY_CC_SIZE); if (0 != memcmp(condBin, targetBinary, binLength)) { fprintf(stderr,"cc_verify error A\n"); diff --git a/src/deprecation.h b/src/deprecation.h index be00c24184b..112d763be4b 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 = 2468000; +static const int APPROX_RELEASE_HEIGHT = 2474000; 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/main.cpp b/src/main.cpp index bdf8545724e..e4a3a381773 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1402,15 +1402,14 @@ bool ContextualCheckTransaction( } // precheck all crypto conditions - bool invalid = false; for (int i = 0; i < tx.vout.size(); i++) { COptCCParams p; if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition(p)) { - if (!p.IsValid()) + if (!p.IsValid(isPBaaS, nHeight) && isPBaaS) { - invalid = true; + return state.DoS(10, error(state.GetRejectReason().c_str()), REJECT_INVALID, "bad-txns-failed-params-precheck"); } if (p.evalCode == EVAL_NONE) { @@ -1424,7 +1423,7 @@ bool ContextualCheckTransaction( CCcontract_info CC; CCcontract_info *cp; if (!((p.evalCode <= EVAL_LAST) && - (cp = CCinit(&CC, p.evalCode)))) + (cp = CCinit(&CC, p.evalCode)))) { return state.DoS(100, error("ContextualCheckTransaction(): Invalid smart transaction eval code"), REJECT_INVALID, "bad-txns-evalcode-invalid"); } @@ -8056,8 +8055,8 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam if (!isExpiringSoon) { // Send stream from relay memory MapRelay::iterator mi; + LOCK(cs_mapRelay); { - LOCK(cs_mapRelay); mi = mapRelay.find(inv.hash); if (mi != mapRelay.end()) { pfrom->PushMessage(inv.GetCommand(), *(*mi).second); diff --git a/src/miner.cpp b/src/miner.cpp index 0d8b03bfcf3..d64d1482afd 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -2389,13 +2389,18 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const std::vecto ? nMedianTimePast : pblock->GetBlockTime(); - if (tx.IsCoinBase() || !IsFinalTx(tx, nHeight, nLockTimeCutoff) || IsExpiredTx(tx, nHeight)) + CValidationState state; + + if (!IsFinalTx(tx, nHeight, nLockTimeCutoff)) { - //fprintf(stderr,"coinbase.%d finaltx.%d expired.%d\n",tx.IsCoinBase(),IsFinalTx(tx, nHeight, nLockTimeCutoff),IsExpiredTx(tx, nHeight)); - if (tx.IsCoinBase() || IsExpiredTx(tx, nHeight)) - { - txesToRemove.push_back(tx); - } + continue; + } + if (tx.IsCoinBase() || + IsExpiredTx(tx, nHeight) || + (mi->GetHeight() != nHeight && + !ContextualCheckTransaction(tx, state, Params(), nHeight, 0))) + { + txesToRemove.push_back(tx); continue; } @@ -2409,8 +2414,6 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const std::vecto CAmount delayedFee = 0; - CValidationState state; - // first, eliminate conflicts at the output level, then we can verify inputs if (isReserve) { diff --git a/src/pbaas/identity.cpp b/src/pbaas/identity.cpp index d6a29923fc2..9707471ba25 100644 --- a/src/pbaas/identity.cpp +++ b/src/pbaas/identity.cpp @@ -2341,9 +2341,10 @@ bool PrecheckIdentityPrimary(const CTransaction &tx, int32_t outNum, CValidation { CIdentity checkIdentity; auto &output = tx.vout[i]; - if (output.scriptPubKey.IsPayToCryptoCondition(p) && + bool isSmartOutput = output.scriptPubKey.IsPayToCryptoCondition(p); + if (isSmartOutput && (!advancedIdentity || p.AsVector().size() < CScript::MAX_SCRIPT_ELEMENT_SIZE) && - p.IsValid() && + p.IsValid(isPBaaS, height) && p.version >= COptCCParams::VERSION_V3 && p.vData.size() > 1) { @@ -2443,9 +2444,13 @@ bool PrecheckIdentityPrimary(const CTransaction &tx, int32_t outNum, CValidation break; } } + else if (advancedIdentity && isSmartOutput && !p.IsValid(isPBaaS, height)) + { + return state.Error("Invalid smart transaction output"); + } } - if (!validIdentity) + if (!validIdentity && identity.IsValid()) { return state.Error("Invalid identity definition"); } diff --git a/src/script/cc.h b/src/script/cc.h index ea0472c0221..f3d0bd0b84d 100644 --- a/src/script/cc.h +++ b/src/script/cc.h @@ -24,8 +24,8 @@ const int CCSigningNodes = 1 << CC_Ed25519 | 1 << CC_Secp256k1; const int CCEvalNode = 1 << CC_Eval; -const int CCFirstEvalOnly = 2; -const int CCLastEvalOnly = 0x0d; +const int CCFirstEvalOnly = 1; +const int CCLastEvalOnly = 0x14; /* * Check if the server can accept the condition based on it's structure / types diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 8fb30185c78..ede1d8c3ade 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1714,15 +1714,6 @@ int TransactionSignatureChecker::CheckCryptoCondition( } if (!success || !condBinary.size()) { - /* - char *jsonCondStr = cc_conditionToJSONString(outputCC); - if (jsonCondStr) - { - printf("Signed condition: %s\n", jsonCondStr); - cJSON_free(jsonCondStr); - } - */ - cc_free(outputCC); condBinary.clear(); } @@ -1766,45 +1757,6 @@ int TransactionSignatureChecker::CheckCryptoCondition( return -1; } - /* - char *jsonCondStr = cc_conditionToJSONString(cond); - if (jsonCondStr) - { - printf("Fulfillment condition: %s\n", jsonCondStr); - cJSON_free(jsonCondStr); - uint8_t buf[2000]; - int ccLen = cc_conditionBinary(cond, buf, 2000); - if (ccLen) - { - CC *transformedCC = cc_readConditionBinary(buf, ccLen); - if (transformedCC) - { - jsonCondStr = cc_conditionToJSONString(transformedCC); - if (jsonCondStr) - { - printf("Converted fulfillment condition: %s\n", jsonCondStr); - cJSON_free(jsonCondStr); - } - cc_free(transformedCC); - } - } - } - if (condBinary.size()) - { - CC *transformedCC = cc_readConditionBinary(condBinary.data(), condBinary.size()); - if (transformedCC) - { - jsonCondStr = cc_conditionToJSONString(transformedCC); - if (jsonCondStr) - { - printf("Binary condition reconstructed: %s\n", jsonCondStr); - cJSON_free(jsonCondStr); - } - cc_free(transformedCC); - } - } - */ - if (!IsSupportedCryptoCondition(cond, p.IsValid() ? p.evalCode : 0) || !IsSignedCryptoCondition(cond)) { cc_free(cond); diff --git a/src/script/script.cpp b/src/script/script.cpp index aefbdd15a98..84c260d8249 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -1564,6 +1564,183 @@ std::vector COptCCParams::GetDestinations() const return destinations; } +bool COptCCParams::IsValid(bool strict, uint32_t nHeight) const +{ + // verify that the output is spendable as a normal smart transaction output with all known combinations and no others + // that may not be spendable + uint32_t solutionVer = CConstVerusSolutionVector::GetVersionByHeight(nHeight); + bool isPBaaS = solutionVer >= CActivationHeight::ACTIVATE_PBAAS; + bool isVault = solutionVer >= CActivationHeight::ACTIVATE_VERUSVAULT; + + bool versionInRange = (!isPBaaS && (version == VERSION_V1 || version == VERSION_V2 || version == VERSION_V3)) || (isPBaaS && version == VERSION_V3); + if (!((isPBaaS || isVault) && strict) || + !versionInRange) + { + return versionInRange; + } + else + { + if (m > n || + n != vKeys.size()) + { + return false; + } + for (auto &oneDest : vKeys) + { + if (oneDest.which() != ADDRTYPE_ID && + oneDest.which() != ADDRTYPE_PK && + oneDest.which() != ADDRTYPE_PKH) + { + return false; + } + } + + if (vData.size() > 1) + { + // back must be a valid master, and if we have more than 2, each must be a valid condition + // no subconditions can contain objects or funds in their scripts + COptCCParams master = COptCCParams(vData.back()); + if (!master.IsValid() || + !(master.evalCode == EVAL_NONE || master.evalCode == EVAL_STAKEGUARD) || + master.m > master.n || + master.n > 4 || + (master.n != (vData.size() - 1) && + !(vData.size() == 2 && + master.n == std::max((int)master.vKeys.size(), 1))) || + master.vData.size()) + { + return false; + } + for (int i = 1; i < (vData.size() - 1); i++) + { + COptCCParams oneParam(vData[i]); + if (!oneParam.IsValid(true, nHeight)) + { + return false; + } + if (oneParam.m > oneParam.n || + oneParam.n > oneParam.vKeys.size()) + { + return false; + } + switch (oneParam.evalCode) + { + case EVAL_STAKEGUARD: + { + // alternate spend + // spendable to public key + if (vData.size() != 3 || + oneParam.vData.size() || + oneParam.vKeys.size() != 1) + { + return false; + } + break; + } + case EVAL_NONE: + case EVAL_IDENTITY_RECOVER: + case EVAL_IDENTITY_REVOKE: + { + if (oneParam.vData.size()) + { + return false; + } + break; + } + case EVAL_NOTARY_EVIDENCE: + { + if (oneParam.vData.size() != 1) + { + return false; + } + CNotaryEvidence evidence(vData[0]); + if (!evidence.IsValid()) + { + return false; + } + break; + } + default: + { + return false; + } + } + for (auto &oneDest : vKeys) + { + if (oneDest.which() != ADDRTYPE_ID && + oneDest.which() != ADDRTYPE_PK && + oneDest.which() != ADDRTYPE_PKH) + { + return false; + } + } + } + return true; + } + else + { + switch (evalCode) + { + case EVAL_STAKEGUARD: + { + if (vData.size() || + vKeys.size() != 1) + { + return false; + } + CCcontract_info CC; + CCcontract_info *cp; + + cp = CCinit(&CC, evalCode); + uint160 evalPKH = CPubKey(ParseHex(CC.CChexstr)).GetID(); + + if ((vKeys[0].which() == ADDRTYPE_PK || vKeys[0].which() == ADDRTYPE_PKH) && GetDestinationID(vKeys[0]) == evalPKH) + { + return true; + } + return false; + } + case EVAL_NONE: + { + if (vData.size() == 1) + { + COptCCParams master = COptCCParams(vData.back()); + if (!master.IsValid() || + master.evalCode != EVAL_NONE) + { + return false; + } + } + return true; + } + case EVAL_IDENTITY_RECOVER: + case EVAL_IDENTITY_REVOKE: + { + if (vData.size()) + { + return false; + } + return true; + } + case EVAL_NOTARY_EVIDENCE: + { + if (vData.size() != 1) + { + return false; + } + CNotaryEvidence evidence(vData[0]); + if (!evidence.IsValid()) + { + return false; + } + return true; + } + } + } + } + return false; +} + bool COptCCParams::IsEvalPKOut() const { if (evalCode > EVAL_LAST) diff --git a/src/script/script.h b/src/script/script.h index b598b573cc4..83d4ec3a38a 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -422,7 +422,7 @@ class CIndexID : public uint160 CIndexID(const uint160& in) : uint160(in) {} }; -/** +/** * A txout script template with a specific destination. It is either: * * CNoDestination: no destination set * * CKeyID: TX_PUBKEYHASH destination @@ -463,12 +463,12 @@ class COptCCParams COptCCParams() : version(0), evalCode(0), m(0), n(0) {} - COptCCParams(uint8_t ver, uint8_t code, uint8_t _m, uint8_t _n, const std::vector &vkeys, const std::vector> &vdata) : + COptCCParams(uint8_t ver, uint8_t code, uint8_t _m, uint8_t _n, const std::vector &vkeys, const std::vector> &vdata) : version(ver), evalCode(code), m(_m), n(_n), vKeys(vkeys), vData(vdata) {} COptCCParams(const std::vector &vch); - bool IsValid() const { return version == VERSION_V1 || version == VERSION_V2 || version == VERSION_V3; } + bool IsValid(bool strict=false, uint32_t nHeight=1) const; std::vector AsVector() const; @@ -499,12 +499,12 @@ class CStakeInfo uint256 prevHash; CStakeInfo() : version(VERSION_INVALID), height(0), sourceHeight(0) {} - CStakeInfo(uint32_t Height, uint32_t SourceHeight, const uint256 &UTXO, const uint256 &PrevHash, uint32_t Version=VERSION_CURRENT) : + CStakeInfo(uint32_t Height, uint32_t SourceHeight, const uint256 &UTXO, const uint256 &PrevHash, uint32_t Version=VERSION_CURRENT) : version(Version), height(0), sourceHeight(0), utxo(UTXO), prevHash(PrevHash) {} CStakeInfo(std::vector vch); std::vector AsVector() const; - + ADD_SERIALIZE_METHODS; template diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 330e2875958..dbc4c01ea29 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -302,9 +302,10 @@ CC *MakeMofNCC(int M, TOBJ1 &condition1, TOBJ2 &condition2, TOBJ3 &condition3, T CScript _CCPubKey(const CC *cond) { - unsigned char buf[2000]; - size_t len = cc_conditionBinary(cond, buf, 2000); - return CScript() << std::vector(buf, buf+len) << OP_CHECKCRYPTOCONDITION; + std::vector buffer(MAX_BINARY_CC_SIZE, 0); + size_t len = cc_conditionBinary(cond, &(buffer[0]), buffer.size()); + buffer.resize(len); + return CScript() << buffer << OP_CHECKCRYPTOCONDITION; } CIdentity LookupIdentity(const BaseSignatureCreator& creator, const CIdentityID &idID) diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 181c3d05f69..140797c3e2e 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -601,7 +601,7 @@ bool ExtractDestinations(const CScript& scriptPubKey, { std::set idSet; - if (p.IsValid() && + if (p.IsValid(true, nHeight) && p.n >= 1 && p.vKeys.size() >= p.n) { @@ -668,6 +668,13 @@ bool ExtractDestinations(const CScript& scriptPubKey, } else if (p.vData.size() && (ccValid = (master = COptCCParams(p.vData.back())).IsValid())) { + // if we have more than master and primary params + int numConditionsNeeded = 1; + if (p.vData.size() > 2) + { + numConditionsNeeded = master.m; + } + // always add the index keys to destinations, but that has nothing to do with whether or not // an ID present in that index represents an address that can sign or spend. ids in this block // are ignored, as all IDs in an output are always indexed in the address and unspent indexes. @@ -770,8 +777,8 @@ bool ExtractDestinations(const CScript& scriptPubKey, } // if this is a compound cc, the master m of n is the top level as an m of n of the sub-conditions - nRequiredRet = p.vData.size() > 2 ? master.m : p.m; - if (canSpendCount >= nRequiredRet && pCanSpend) + nRequiredRet = numConditionsNeeded; + if (canSpendCount >= numConditionsNeeded && pCanSpend) { *pCanSpend = true; } @@ -805,7 +812,18 @@ bool ExtractDestinations(const CScript& scriptPubKey, } else { - return false; + // if it's a bad smart transaction output, we can't spend it, but + // that doesn't mean that we should return false unless we are PBaaS or later + // for compatibility + if (pCanSign) + { + *pCanSign = false; + } + if (pCanSpend) + { + *pCanSpend = false; + } + return CConstVerusSolutionVector::GetVersionByHeight(nHeight) < CActivationHeight::ACTIVATE_PBAAS; } } else diff --git a/src/version.h b/src/version.h index fd73b23d9de..56b7c21c0f7 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-4" +#define VERUS_VERSION "0.9.9-5" #endif // BITCOIN_VERSION_H