// Copyright (c) 2016-2020 The Hush developers // Distributed under the GPLv3 software license, see the accompanying // file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html #ifndef HEIR_VALIDATE_H #define HEIR_VALIDATE_H #include "CCinclude.h" #include "CCHeir.h" #define IS_CHARINSTR(c, str) (std::string(str).find((char)(c)) != std::string::npos) // makes coin initial tx opret vscript_t EncodeHeirCreateOpRet(uint8_t funcid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName, std::string memo); vscript_t EncodeHeirOpRet(uint8_t funcid, uint256 fundingtxid, uint8_t isHeirSpendingBegan); uint256 FindLatestFundingTx(uint256 fundingtxid, uint256 &tokenid, CScript& opRetScript, uint8_t &isHeirSpendingBegan); //uint8_t DecodeHeirOpRet(CScript scriptPubKey, uint256& fundingtxid, uint8_t &isHeirSpendingBegan, bool noLogging = false); //uint8_t DecodeHeirOpRet(CScript scriptPubKey, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, bool noLogging = false); uint8_t DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, std::string& memo, bool noLogging = false); uint8_t DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, uint256 &fundingTxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging = false); inline static bool isMyFuncId(uint8_t funcid) { return IS_CHARINSTR(funcid, "FAC"); } inline static bool isSpendingTx(uint8_t funcid) { return (funcid == 'C'); } // helper class to allow polymorphic behaviour for HeirXXX() functions in case of coins class CoinHelper { public: static uint8_t getMyEval() { return EVAL_HEIR; } static int64_t addOwnerInputs(uint256 dummyid, CMutableTransaction& mtx, CPubKey ownerPubkey, int64_t total, int32_t maxinputs) { return AddNormalinputs(mtx, ownerPubkey, total, maxinputs); } static CScript makeCreateOpRet(uint256 dummyid, std::vector dummyPubkeys, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName, std::string memo) { return CScript() << OP_RETURN << EncodeHeirCreateOpRet((uint8_t)'F', ownerPubkey, heirPubkey, inactivityTimeSec, heirName, memo); } static CScript makeAddOpRet(uint256 dummyid, std::vector dummyPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { return CScript() << OP_RETURN << EncodeHeirOpRet((uint8_t)'A', fundingtxid, isHeirSpendingBegan); } static CScript makeClaimOpRet(uint256 dummyid, std::vector dummyPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { return CScript() << OP_RETURN << EncodeHeirOpRet((uint8_t)'C', fundingtxid, isHeirSpendingBegan); } static CTxOut make1of2Vout(int64_t amount, CPubKey ownerPubkey, CPubKey heirPubkey) { return MakeCC1of2vout(EVAL_HEIR, amount, ownerPubkey, heirPubkey); } static CTxOut makeUserVout(int64_t amount, CPubKey myPubkey) { return CTxOut(amount, CScript() << ParseHex(HexStr(myPubkey)) << OP_CHECKSIG); } /* static CTxOut makeClaimerVout(int64_t amount, CPubKey myPubkey) { return CTxOut(amount, CScript() << ParseHex(HexStr(myPubkey)) << OP_CHECKSIG); } */ static bool GetCoinsOrTokensCCaddress1of2(char *coinaddr, CPubKey ownerPubkey, CPubKey heirPubkey) { struct CCcontract_info *cpHeir, heirC; cpHeir = CCinit(&heirC, EVAL_HEIR); return GetCCaddress1of2(cpHeir, coinaddr, ownerPubkey, heirPubkey); } static void CCaddrCoinsOrTokens1of2set(struct CCcontract_info *cp, CPubKey ownerPubkey, CPubKey heirPubkey, char *coinaddr) { uint8_t mypriv[32]; Myprivkey(mypriv); CCaddr1of2set(cp, ownerPubkey, heirPubkey,mypriv, coinaddr); memset(mypriv,0,sizeof(mypriv)); } }; // helper class to allow polymorphic behaviour for HeirXXX() functions in case of tokens class TokenHelper { public: static uint8_t getMyEval() { return EVAL_TOKENS; } static int64_t addOwnerInputs(uint256 tokenid, CMutableTransaction& mtx, CPubKey ownerPubkey, int64_t total, int32_t maxinputs) { struct CCcontract_info *cpHeir, heirC; cpHeir = CCinit(&heirC, EVAL_TOKENS); return AddTokenCCInputs(cpHeir, mtx, ownerPubkey, tokenid, total, maxinputs); } static CScript makeCreateOpRet(uint256 tokenid, std::vector voutTokenPubkeys, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName, std::string memo) { return EncodeTokenOpRet(tokenid, voutTokenPubkeys, std::make_pair(OPRETID_HEIRDATA, EncodeHeirCreateOpRet((uint8_t)'F', ownerPubkey, heirPubkey, inactivityTimeSec, heirName, memo))); } static CScript makeAddOpRet(uint256 tokenid, std::vector voutTokenPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { return EncodeTokenOpRet(tokenid, voutTokenPubkeys, std::make_pair(OPRETID_HEIRDATA, EncodeHeirOpRet((uint8_t)'A', fundingtxid, isHeirSpendingBegan))); } static CScript makeClaimOpRet(uint256 tokenid, std::vector voutTokenPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { return EncodeTokenOpRet(tokenid, voutTokenPubkeys, std::make_pair(OPRETID_HEIRDATA, EncodeHeirOpRet((uint8_t)'C', fundingtxid, isHeirSpendingBegan))); } static CTxOut make1of2Vout(int64_t amount, CPubKey ownerPubkey, CPubKey heirPubkey) { return MakeTokensCC1of2vout(EVAL_HEIR, amount, ownerPubkey, heirPubkey); } static CTxOut makeUserVout(int64_t amount, CPubKey myPubkey) { return MakeCC1vout(EVAL_TOKENS, amount, myPubkey); // yes EVAL_TOKENS } /* static CTxOut makeClaimerVout(int64_t amount, CPubKey myPubkey) { return MakeCC1vout(EVAL_TOKENS, amount, myPubkey); // yes EVAL_TOKENS } */ static bool GetCoinsOrTokensCCaddress1of2(char *coinaddr, CPubKey ownerPubkey, CPubKey heirPubkey) { struct CCcontract_info *cpHeir, heirC; cpHeir = CCinit(&heirC, EVAL_HEIR); return GetTokensCCaddress1of2(cpHeir, coinaddr, ownerPubkey, heirPubkey); } static void CCaddrCoinsOrTokens1of2set(struct CCcontract_info *cp, CPubKey ownerPubkey, CPubKey heirPubkey, char *coinaddr) { uint8_t mypriv[32]; Myprivkey(mypriv); CCaddrTokens1of2set(cp, ownerPubkey, heirPubkey, mypriv, coinaddr); memset(mypriv,0,sizeof(mypriv)); } }; /** * Small framework for vins and vouts validation implementing a variation of 'chain of responsibility' pattern: * It consists of two classes CInputValidationPlan and COutputValidationPlan which both are configured with an array of vectors of validators * (These validators are derived from the class CValidatorBase). * * A example of a validator may verify for a vout if its public key corresponds to the public key which is stored in opreturn. * Or, vin validator may check if this vin depicts correctly to the CC contract's address. * * For validating vins CInputValidator additionally is provided with an instance of a class derived from the CInputIdentifierBase class. * this identifier class allows to select identical vins (for example, normal vins or cc input vins) and apply validators from the corresponding vector to it. * Note: CInputValidator treats that at least one identified vin should be present, otherwise it returns eval->invalid() and false. * * For validating vouts COutputValidator is configured for each vector of validators with the vout index to which these validators are applied * (see constructors of both CInputValidator and COutputValidator) */ /** * base class for all validators */ class CValidatorBase { public: CValidatorBase(CCcontract_info* cp) : m_cp(cp) {} virtual bool isVinValidator() const = 0; virtual bool validateVin(CTxIn vin, std::vector prevVout, int32_t prevN, std::string& message) const = 0; virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const = 0; protected: CCcontract_info * m_cp; }; /** * Base class for classes which identify vins as normal or cc inputs */ class CInputIdentifierBase { public: CInputIdentifierBase(CCcontract_info* cp) : m_cp(cp) {} virtual std::string inputName() const = 0; virtual bool identifyInput(CTxIn vin) const = 0; protected: CCcontract_info * m_cp; }; /** * Encapsulates an array containing rows of validators * Each row is a vector of validators (zero is possible) for validating vins or prev tx's vouts * this validation plan is used for validating tx inputs */ template class CInputValidationPlan { using ValidatorsRow = std::vector; public: // Pushes a row of validators for validating a vin or vout // @param CInputIdentifierBase* pointer to class-identifier which determines several identical adjacent vins (like in schema "vin.0+: normal inputs") // @param pargs parameter pack of zero or more pointer to validator objects // Why pointers? because we store the base class in validators' row and then call its virtual functions template void pushValidators(CInputIdentifierBase *identifier, ARGS*... pargs) // validators row passed as variadic arguments CValidatorX *val1, CValidatorY *val2 ... { ValidatorsRow vValidators({ (TValidatorBase*)pargs... }); m_arrayValidators.push_back(std::make_pair(identifier, vValidators)); } // validate tx inputs and corresponding prev tx vouts bool validate(const CTransaction& tx, Eval* eval) { std::string message = ""; //std::cerr << "CInputValidationPlan::validate() starting vins validation..." << std::endl; int32_t ival = 0; int32_t iv = 0; int32_t numv = tx.vin.size(); int32_t numValidators = m_arrayValidators.size(); // run over vins: while (iv < numv && ival < numValidators) { int32_t identifiedCount = 0; CInputIdentifierBase *identifier = m_arrayValidators[ival].first; // check if this is 'our' input: while (iv < numv && identifier->identifyInput(tx.vin[iv])) { // get prev tx: CTransaction prevTx, *pPrevTxOrNull = NULL; uint256 hashBlock; if (!eval->GetTxUnconfirmed(tx.vin[iv].prevout.hash, prevTx, hashBlock)) { std::ostringstream stream; stream << "can't find vinTx for vin=" << iv << "."; return eval->Invalid(stream.str().c_str()); } pPrevTxOrNull = &prevTx; // TODO: get prev tx only if it required (i.e. if vout validators are present) // exec 'validators' from validator row of ival index, for tx.vin[iv] if (!execValidatorsInRow(&tx, pPrevTxOrNull, iv, ival, message)) { std::ostringstream stream; stream << "invalid tx vin[" << iv << "]:" << message; return eval->Invalid(stream.str().c_str()); // ... if not, return 'invalid' } identifiedCount++; // how many vins we identified iv++; // advance to the next vin } // CInputValidationPlan treats that there must be at least one identified vin for configured validators' row // like in 'vin.0: normal input' if (identifiedCount == 0) { std::ostringstream stream; stream << "can't find required vins for " << identifier->inputName() << "."; return eval->Invalid(stream.str().c_str()); } ival++; // advance to the next validator row // and it will try the same vin with the new CInputIdentifierBase and validators row } // validation is successful if all validators have been used (i.e. ival = numValidators) if (ival < numValidators) { std::cerr << "CInputValidationPlan::validate() incorrect tx" << " ival=" << ival << " numValidators=" << numValidators << std::endl; return eval->Invalid("incorrect tx structure: not all required vins are present."); } //std::cerr << "CInputValidationPlan::validate() returns with true" << std::endl; return true; } private: // Executes validators from the requested row of validators (selected by iValidators) for selected vin or vout (selected by iv) bool execValidatorsInRow(const CTransaction* pTx, const CTransaction* pPrevTx, int32_t iv, int32_t ival, std::string& refMessage) const { // check boundaries: if (ival < 0 || ival >= m_arrayValidators.size()) { std::cerr << "CInputValidationPlan::execValidatorsInRow() internal error: incorrect param ival=" << ival << " size=" << m_arrayValidators.size(); refMessage = "internal error: incorrect param ival index"; return false; } if (iv < 0 || iv >= pTx->vin.size()) { std::cerr << "CInputValidationPlan::execValidatorsInRow() internal error: incorrect param iv=" << iv << " size=" << m_arrayValidators.size(); refMessage = "internal error: incorrect param iv index"; return false; } // get requested row of validators: ValidatorsRow vValidators = m_arrayValidators[ival].second; //std::cerr << "CInputValidationPlan::execValidatorsInRow() calling validators" << " for vin iv=" << iv << " ival=" << ival << std::endl; for (auto v : vValidators) { bool result; if (v->isVinValidator()) // validate this vin and previous vout: result = v->validateVin(pTx->vin[iv], pPrevTx->vout, pTx->vin[iv].prevout.n, refMessage); else // if it is vout validator pass the previous tx vout: result = v->validateVout( pPrevTx->vout[pTx->vin[iv].prevout.n], pTx->vin[iv].prevout.n, refMessage); if (!result) { return result; } } return true; // validation OK } private: //std::map m_arrayValidators; std::vector< std::pair > m_arrayValidators; }; /** * Encapsulates an array containing rows of validators * Each row is a vector of validators (zero is possible) for validating vouts * this validation plan is used for validating tx outputs */ template class COutputValidationPlan { using ValidatorsRow = std::vector; public: // Pushes a row of validators for validating a vout // @param ivout index to vout to validate // @param pargs parameter pack of zero or more pointer to validator objects // Why pointers? because we store base class and call its virtual functions template void pushValidators(int32_t ivout, ARGS*... pargs) // validators row passed as variadic arguments CValidatorX *val1, CValidatorY *val2 ... { ValidatorsRow vValidators({ (TValidatorBase*)pargs... }); m_arrayValidators.push_back(std::make_pair(ivout, vValidators)); } // validate tx outputs bool validate(const CTransaction& tx, Eval* eval) { std::string message = ""; //std::cerr << "COutputValidationPlan::validateOutputs() starting vouts validation..." << std::endl; int32_t ival = 0; int32_t numVouts = tx.vout.size(); int32_t numValidators = m_arrayValidators.size(); // run over vouts: while (ival < numValidators) { int32_t ivout = m_arrayValidators[ival].first; if (ivout >= numVouts) { std::cerr << "COutputValidationPlan::validate() incorrect tx" << "for ival=" << ival << " in tx.vout no such ivout=" << ivout << std::endl; return eval->Invalid("incorrect tx structure: not all required vouts are present."); } else { // exec 'validators' from validator row of ival index, for tx.vout[ivout] if (!execValidatorsInRow(&tx, ivout, ival, message)) { std::ostringstream stream; stream << "invalid tx vout[" << ivout << "]:" << message; return eval->Invalid(stream.str().c_str()); // ... if not, return 'invalid' } } ival++; // advance to the next vout } //std::cerr << "COutputValidationPlan::validate() returns with true" << std::endl; return true; } private: // Executes validators from the requested row of validators (selected by iValidators) for selected vin or vout (selected by iv) bool execValidatorsInRow(const CTransaction* pTx, int32_t iv, int32_t ival, std::string& refMessage) const { // check boundaries: if (ival < 0 || ival >= m_arrayValidators.size()) { std::cerr << "COutputValidationPlan::execValidatorsInRow() internal error: incorrect param ival=" << ival << " size=" << m_arrayValidators.size(); refMessage = "internal error: incorrect param ival index"; return false; } if (iv < 0 || iv >= pTx->vout.size()) { std::cerr << "COutputValidationPlan::execValidatorsInRow() internal error: incorrect param iv=" << iv << " size=" << m_arrayValidators.size(); refMessage = "internal error: incorrect param iv index"; return false; } // get requested row of validators: ValidatorsRow vValidators = m_arrayValidators[ival].second; //std::cerr << "COutputValidationPlan::execRow() calling validators" << " for vout iv=" << iv << " ival=" << ival << std::endl; for (auto v : vValidators) { if (!v->isVinValidator()) { // if this is a 'in' validation plan then pass the previous tx vout: bool result = v->validateVout(pTx->vout[iv], iv, refMessage); if (!result) return result; } } return true; // validation OK } private: //std::map m_mapValidators; std::vector< std::pair > m_arrayValidators; }; class CNormalInputIdentifier : CInputIdentifierBase { public: CNormalInputIdentifier(CCcontract_info* cp) : CInputIdentifierBase(cp) {} virtual std::string inputName() const { return std::string("normal input"); } virtual bool identifyInput(CTxIn vin) const { return !IsCCInput(vin.scriptSig); } }; class CCCInputIdentifier : CInputIdentifierBase { public: CCCInputIdentifier(CCcontract_info* cp) : CInputIdentifierBase(cp) {} virtual std::string inputName() const { return std::string("CC input"); } virtual bool identifyInput(CTxIn vin) const { return IsCCInput(vin.scriptSig); } }; /** * Validates 1of2address for vout (may be used for either this or prev tx) */ template class CCC1of2AddressValidator : CValidatorBase { public: CCC1of2AddressValidator(CCcontract_info* cp, CScript opRetScript, std::string customMessage = "") : m_fundingOpretScript(opRetScript), m_customMessage(customMessage), CValidatorBase(cp) {} virtual bool isVinValidator() const { return false; } virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const { //std::cerr << "CCC1of2AddressValidator::validateVout() entered" << std::endl; CPubKey ownerPubkey, heirPubkey; int64_t inactivityTime; std::string heirName, memo; uint256 tokenid; uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, true); if (funcId == 0) { message = m_customMessage + std::string(" invalid opreturn format"); std::cerr << "CCC1of2AddressValidator::validateVout() exits with false: " << message << std::endl; return false; } char shouldBeAddr[65], ccAddr[65]; //GetCCaddress1of2(m_cp, shouldBeAddr, ownerPubkey, heirPubkey); Helper::GetCoinsOrTokensCCaddress1of2(shouldBeAddr, ownerPubkey, heirPubkey); if (vout.scriptPubKey.IsPayToCryptoCondition()) { if (Getscriptaddress(ccAddr, vout.scriptPubKey) && strcmp(shouldBeAddr, ccAddr) == 0) { //std::cerr << "CCC1of2AddressValidator::validateVout() exits with true" << std::endl; return true; } else { message = m_customMessage + std::string(" incorrect heir funding address: incorrect pubkey(s)"); } } else { message = m_customMessage + std::string(" incorrect heir funding address: not a 1of2addr"); } std::cerr << "CCC1of2AddressValidator::validateVout() exits with false: " << message << std::endl; return false; } virtual bool validateVin(CTxIn vin, std::vector prevVout, int32_t prevN, std::string& message) const { return false; } private: CScript m_fundingOpretScript; std::string m_customMessage; }; /** * Validates if this is vout to owner or heir from opret (funding or change) */ template class CMyPubkeyVoutValidator : CValidatorBase { public: CMyPubkeyVoutValidator(CCcontract_info* cp, CScript opRetScript, bool checkNormals) : m_fundingOpretScript(opRetScript), m_checkNormals(checkNormals), CValidatorBase(cp) { } virtual bool isVinValidator() const { return false; } virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const { //std::cerr << "CMyPubkeyVoutValidator::validateVout() entered" << std::endl; CPubKey ownerPubkey, heirPubkey; int64_t inactivityTime; std::string heirName, memo; uint256 tokenid; ///std::cerr << "CMyPubkeyVoutValidator::validateVout() m_opRetScript=" << m_opRetScript.ToString() << std::endl; // get both pubkeys: uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, true); if (funcId == 0) { message = std::string("invalid opreturn format"); return false; } CScript ownerScript; CScript heirScript; if (m_checkNormals) { //not used, incorrect check, too strict ownerScript = CoinHelper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey; heirScript = CoinHelper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey; std::cerr << "CMyPubkeyVoutValidator::validateVout() vout.scriptPubKey=" << vout.scriptPubKey.ToString() << " makeUserVout(coin,owner)=" << CoinHelper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey.ToString() << " makeUserVout(coin,heir)=" << CoinHelper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey.ToString() << std::endl; } else { ownerScript = Helper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey; heirScript = Helper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey; std::cerr << "CMyPubkeyVoutValidator::validateVout() vout.scriptPubKey=" << vout.scriptPubKey.ToString() << " makeUserVout(owner)=" << Helper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey.ToString() << " makeUserVout(heir)=" << Helper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey.ToString() << std::endl; } // recreate scriptPubKey for owner and heir and compare it with that of the vout to check: if (vout.scriptPubKey == ownerScript || vout.scriptPubKey == heirScript) { // this is vout to owner or heir addr: //std::cerr << "CMyPubkeyVoutValidator::validateVout() exits with true" << std::endl; return true; } std::cerr << "CMyPubkeyVoutValidator::validateVout() exits with false (not the owner's or heir's addresses)" << std::endl; message = std::string("invalid pubkey"); return false; } virtual bool validateVin(CTxIn vin, std::vector prevVout, int32_t prevN, std::string& message) const { return true; } private: CScript m_fundingOpretScript; //uint256 m_lasttxid; bool m_checkNormals; }; /** * Check if the user is the heir and the heir is allowed to spend (duration > inactivityTime) */ template class CHeirSpendValidator : CValidatorBase { public: CHeirSpendValidator(CCcontract_info* cp, CScript opRetScript, uint256 latesttxid, uint8_t isHeirSpendingBegan) : m_fundingOpretScript(opRetScript), m_latesttxid(latesttxid), m_isHeirSpendingBegan(isHeirSpendingBegan), CValidatorBase(cp) {} virtual bool isVinValidator() const { return false; } virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const { //std::cerr << "CHeirSpendValidator::validateVout() entered" << std::endl; CPubKey ownerPubkey, heirPubkey; int64_t inactivityTime; std::string heirName, memo; uint256 tokenid; // get heir pubkey: uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, true); if (funcId == 0) { message = std::string("invalid opreturn format"); return false; } int32_t numblocks; int64_t durationSec = CCduration(numblocks, m_latesttxid); // recreate scriptPubKey for heir and compare it with that of the vout: if (vout.scriptPubKey == Helper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey) { // this is the heir is trying to spend if (!m_isHeirSpendingBegan && durationSec <= inactivityTime) { message = "heir is not allowed yet to spend funds"; std::cerr << "CHeirSpendValidator::validateVout() heir is not allowed yet to spend funds" << std::endl; return false; } else { // heir is allowed to spend return true; } } //std::cerr << "CHeirSpendValidator::validateVout() exits with true" << std::endl; // this is not heir: return true; } virtual bool validateVin(CTxIn vin, std::vector prevVout, int32_t prevN, std::string& message) const { return true; } private: CScript m_fundingOpretScript; uint256 m_latesttxid; uint8_t m_isHeirSpendingBegan; }; /** * Validates this opreturn and compares it with the opreturn from the previous tx */ template class COpRetValidator : CValidatorBase { public: COpRetValidator(CCcontract_info* cp, CScript opret) : m_fundingOpretScript(opret), CValidatorBase(cp) {} virtual bool isVinValidator() const { return false; } virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const { //std::cerr << "COpRetValidator::validateVout() entered" << std::endl; uint256 fundingTxidInOpret = zeroid, dummyTxid, tokenid = zeroid, initialTokenid = zeroid; uint8_t dummyIsHeirSpendingBegan; uint8_t funcId = DecodeHeirEitherOpRet(vout.scriptPubKey, tokenid, fundingTxidInOpret, dummyIsHeirSpendingBegan, true); if (funcId == 0) { message = std::string("invalid opreturn format"); return false; } uint8_t initialFuncId = DecodeHeirEitherOpRet(m_fundingOpretScript, initialTokenid, dummyTxid, dummyIsHeirSpendingBegan, true); if (initialFuncId == 0) { message = std::string("invalid initial tx opreturn format"); return false; } // validation rules: if (!isMyFuncId(funcId)) { message = std::string("invalid funcid in opret"); return false; } if (typeid(Helper) == typeid(TokenHelper)) { if (tokenid != initialTokenid) { message = std::string("invalid tokenid in opret"); return false; } } //std::cerr << "COpRetValidator::validateVout() exits with true" << std::endl; return true; } virtual bool validateVin(CTxIn vin, std::vector prevVout, int32_t prevN, std::string& message) const { return true; } private: CScript m_fundingOpretScript; }; /** * marker spending prevention validator, * returns false if for tx with funcid=F vout.1 is being tried to spend */ template class CMarkerValidator : CValidatorBase { public: CMarkerValidator(CCcontract_info* cp) : CValidatorBase(cp) { } virtual bool isVinValidator() const { return true; } // this is vin validator virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const { return true; } virtual bool validateVin(CTxIn vin, std::vector prevVout, int32_t prevN, std::string& message) const { uint256 fundingTxidInOpret = zeroid, dummyTxid, tokenid = zeroid, initialTokenid = zeroid; uint8_t dummyIsHeirSpendingBegan; //std::cerr << "CMarkerValidator::validateVin() prevVout.size()=" << prevVout.size() << " prevN=" << prevN << std::endl; if (prevVout.size() > 0) { // get funcId for prev tx: uint8_t funcId = DecodeHeirEitherOpRet(prevVout[prevVout.size()-1].scriptPubKey, tokenid, fundingTxidInOpret, dummyIsHeirSpendingBegan, true); //std::cerr << "CMarkerValidator::validateVin() funcId=" << (funcId?funcId:' ') << std::endl; if (funcId == 'F' && prevN == 1) { // do not allow to spend 'F' marker's vout message = std::string("spending marker not allowed"); return false; } } //std::cerr << "CMarkerValidator::validateVin() exits with true" << std::endl; return true; } }; /** * empty validator always returns true */ template class CNullValidator : CValidatorBase { public: CNullValidator(CCcontract_info* cp) : CValidatorBase(cp) { } virtual bool isVinValidator() const { return false; } virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const { return true; } virtual bool validateVin(CTxIn vin, std::vector prevVout, int32_t prevN, std::string& message) const { return true; } }; #endif