diff --git a/src/cc/heir_validate.h b/src/cc/heir_validate.h index 85f5b5c11..f8e66e3d5 100644 --- a/src/cc/heir_validate.h +++ b/src/cc/heir_validate.h @@ -22,100 +22,100 @@ 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) { - return EncodeHeirCreateOpRet((uint8_t)'F', ownerPubkey, heirPubkey, inactivityTimeSec, heirName); - } - static CScript makeAddOpRet(uint256 dummyid, std::vector dummyPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { - return EncodeHeirOpRet((uint8_t)'A', fundingtxid, isHeirSpendingBegan); - } - static CScript makeClaimOpRet(uint256 dummyid, std::vector dummyPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { - 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 CC_info *cpHeir, heirC; - cpHeir = CCinit(&heirC, EVAL_HEIR); - return GetCCaddress1of2(cpHeir, coinaddr, ownerPubkey, heirPubkey); - } - static void CCaddrCoinsOrTokens1of2set(struct CC_info *cp, CPubKey ownerPubkey, CPubKey heirPubkey, char *coinaddr) { - CCaddr1of2set(cp, ownerPubkey, heirPubkey, coinaddr); - } + + 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) { + return EncodeHeirCreateOpRet((uint8_t)'F', ownerPubkey, heirPubkey, inactivityTimeSec, heirName); + } + static CScript makeAddOpRet(uint256 dummyid, std::vector dummyPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { + return EncodeHeirOpRet((uint8_t)'A', fundingtxid, isHeirSpendingBegan); + } + static CScript makeClaimOpRet(uint256 dummyid, std::vector dummyPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { + 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 CC_info *cpHeir, heirC; + cpHeir = CCinit(&heirC, EVAL_HEIR); + return GetCCaddress1of2(cpHeir, coinaddr, ownerPubkey, heirPubkey); + } + static void CCaddrCoinsOrTokens1of2set(struct CC_info *cp, CPubKey ownerPubkey, CPubKey heirPubkey, char *coinaddr) { + CCaddr1of2set(cp, ownerPubkey, heirPubkey, coinaddr); + } }; // 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 CC_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) { - return EncodeTokenOpRet(tokenid, voutTokenPubkeys, - EncodeHeirCreateOpRet((uint8_t)'F', ownerPubkey, heirPubkey, inactivityTimeSec, heirName)); - } - static CScript makeAddOpRet(uint256 tokenid, std::vector voutTokenPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { - return EncodeTokenOpRet(tokenid, voutTokenPubkeys, - EncodeHeirOpRet((uint8_t)'A', fundingtxid, isHeirSpendingBegan)); - } - static CScript makeClaimOpRet(uint256 tokenid, std::vector voutTokenPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { - return EncodeTokenOpRet(tokenid, voutTokenPubkeys, - 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 CC_info *cpHeir, heirC; - cpHeir = CCinit(&heirC, EVAL_HEIR); - return GetTokensCCaddress1of2(cpHeir, coinaddr, ownerPubkey, heirPubkey); - } - - static void CCaddrCoinsOrTokens1of2set(struct CC_info *cp, CPubKey ownerPubkey, CPubKey heirPubkey, char *coinaddr) { - - CCaddrTokens1of2set(cp, ownerPubkey, heirPubkey, coinaddr); - } + static uint8_t getMyEval() { return EVAL_TOKENS; } + static int64_t addOwnerInputs(uint256 tokenid, CMutableTransaction& mtx, CPubKey ownerPubkey, int64_t total, int32_t maxinputs) { + struct CC_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) { + return EncodeTokenOpRet(tokenid, voutTokenPubkeys, + EncodeHeirCreateOpRet((uint8_t)'F', ownerPubkey, heirPubkey, inactivityTimeSec, heirName)); + } + static CScript makeAddOpRet(uint256 tokenid, std::vector voutTokenPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { + return EncodeTokenOpRet(tokenid, voutTokenPubkeys, + EncodeHeirOpRet((uint8_t)'A', fundingtxid, isHeirSpendingBegan)); + } + static CScript makeClaimOpRet(uint256 tokenid, std::vector voutTokenPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { + return EncodeTokenOpRet(tokenid, voutTokenPubkeys, + 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 CC_info *cpHeir, heirC; + cpHeir = CCinit(&heirC, EVAL_HEIR); + return GetTokensCCaddress1of2(cpHeir, coinaddr, ownerPubkey, heirPubkey); + } + + static void CCaddrCoinsOrTokens1of2set(struct CC_info *cp, CPubKey ownerPubkey, CPubKey heirPubkey, char *coinaddr) { + + CCaddrTokens1of2set(cp, ownerPubkey, heirPubkey, coinaddr); + } }; /** -* 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) -*/ + * 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 @@ -123,13 +123,13 @@ public: class CValidatorBase { public: - CValidatorBase(CC_info* cp) : m_cp(cp) {} - virtual bool isVinValidator() const = 0; - virtual bool validateVin(CTxIn vin, CTxOut prevVout, std::string& message) const = 0; - virtual bool validateVout(CTxOut vout, std::string& message) const = 0; - + CValidatorBase(CCcontract_info* cp) : m_cp(cp) {} + virtual bool isVinValidator() const = 0; + virtual bool validateVin(CTxIn vin, CTxOut prevVout, std::string& message) const = 0; + virtual bool validateVout(CTxOut vout, std::string& message) const = 0; + protected: - CC_info * m_cp; + CCcontract_info * m_cp; }; /** @@ -138,493 +138,493 @@ protected: class CInputIdentifierBase { public: - CInputIdentifierBase(CC_info* cp) : m_cp(cp) {} - virtual std::string inputName() const = 0; - virtual bool identifyInput(CTxIn vin) const = 0; + CInputIdentifierBase(CCcontract_info* cp) : m_cp(cp) {} + virtual std::string inputName() const = 0; + virtual bool identifyInput(CTxIn vin) const = 0; protected: - CC_info * m_cp; + 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 -*/ + * 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; - + 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; - } - + + // 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], refMessage); - if (!result) { - return result; - } - } - return true; // validation OK - } - - + // 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], refMessage); + if (!result) { + return result; + } + } + return true; // validation OK + } + + private: - //std::map m_arrayValidators; - std::vector< std::pair > m_arrayValidators; - + //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 -*/ + * 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; - + 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; - } - + // 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], refMessage); - if (!result) - return result; - } - } - return true; // validation OK - } - + // 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], refMessage); + if (!result) + return result; + } + } + return true; // validation OK + } + private: - //std::map m_mapValidators; - std::vector< std::pair > m_arrayValidators; - + //std::map m_mapValidators; + std::vector< std::pair > m_arrayValidators; + }; class CNormalInputIdentifier : CInputIdentifierBase { public: - CNormalInputIdentifier(CC_info* cp) : CInputIdentifierBase(cp) {} - virtual std::string inputName() const { return std::string("normal input"); } - virtual bool identifyInput(CTxIn vin) const { - return !IsCCInput(vin.scriptSig); - } + 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(CC_info* cp) : CInputIdentifierBase(cp) {} - virtual std::string inputName() const { return std::string("CC input"); } - virtual bool identifyInput(CTxIn vin) const { - return IsCCInput(vin.scriptSig); - } + 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) -*/ + * Validates 1of2address for vout (may be used for either this or prev tx) + */ template class CCC1of2AddressValidator : CValidatorBase { public: - CCC1of2AddressValidator(CC_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, std::string& message) const - { - //std::cerr << "CCC1of2AddressValidator::validateVout() entered" << std::endl; - CPubKey ownerPubkey, heirPubkey; - int64_t inactivityTime; - std::string heirName; - uint256 tokenid; - - uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, 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, CTxOut prevVout, std::string& message) const { return false; } - + 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, std::string& message) const + { + //std::cerr << "CCC1of2AddressValidator::validateVout() entered" << std::endl; + CPubKey ownerPubkey, heirPubkey; + int64_t inactivityTime; + std::string heirName; + uint256 tokenid; + + uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, 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, CTxOut prevVout, std::string& message) const { return false; } + private: - CScript m_fundingOpretScript; - std::string m_customMessage; + CScript m_fundingOpretScript; + std::string m_customMessage; }; /** -* Validates if this is vout to owner or heir from opret (funding or change) -*/ + * Validates if this is vout to owner or heir from opret (funding or change) + */ template class CMyPubkeyVoutValidator : CValidatorBase { public: - CMyPubkeyVoutValidator(CC_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, std::string& message) const - { - //std::cerr << "CMyPubkeyVoutValidator::validateVout() entered" << std::endl; - - CPubKey ownerPubkey, heirPubkey; - int64_t inactivityTime; - std::string heirName; - 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, 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, CTxOut prevVout, std::string& message) const { return true; } - + 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, std::string& message) const + { + //std::cerr << "CMyPubkeyVoutValidator::validateVout() entered" << std::endl; + + CPubKey ownerPubkey, heirPubkey; + int64_t inactivityTime; + std::string heirName; + 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, 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, CTxOut prevVout, std::string& message) const { return true; } + private: - CScript m_fundingOpretScript; - //uint256 m_lasttxid; - bool m_checkNormals; + 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) -*/ + * Check if the user is the heir and the heir is allowed to spend (duration > inactivityTime) + */ template class CHeirSpendValidator : CValidatorBase { public: - CHeirSpendValidator(CC_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, std::string& message) const - { - //std::cerr << "CHeirSpendValidator::validateVout() entered" << std::endl; - - CPubKey ownerPubkey, heirPubkey; - int64_t inactivityTime; - std::string heirName; - uint256 tokenid; - - // get heir pubkey: - uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, 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, CTxOut prevVout, std::string& message) const { return true; } - + 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, std::string& message) const + { + //std::cerr << "CHeirSpendValidator::validateVout() entered" << std::endl; + + CPubKey ownerPubkey, heirPubkey; + int64_t inactivityTime; + std::string heirName; + uint256 tokenid; + + // get heir pubkey: + uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, 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, CTxOut prevVout, std::string& message) const { return true; } + private: - CScript m_fundingOpretScript; - uint256 m_latesttxid; - uint8_t m_isHeirSpendingBegan; + CScript m_fundingOpretScript; + uint256 m_latesttxid; + uint8_t m_isHeirSpendingBegan; }; /** -* Validates this opreturn and compares it with the opreturn from the previous tx -*/ + * Validates this opreturn and compares it with the opreturn from the previous tx + */ template class COpRetValidator : CValidatorBase { public: - COpRetValidator(CC_info* cp, CScript opret) - : m_fundingOpretScript(opret), CValidatorBase(cp) {} - - virtual bool isVinValidator() const { return false; } - virtual bool validateVout(CTxOut vout, 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, CTxOut prevVout, std::string& message) const { return true; } - + COpRetValidator(CCcontract_info* cp, CScript opret) + : m_fundingOpretScript(opret), CValidatorBase(cp) {} + + virtual bool isVinValidator() const { return false; } + virtual bool validateVout(CTxOut vout, 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, CTxOut prevVout, std::string& message) const { return true; } + private: - CScript m_fundingOpretScript; + CScript m_fundingOpretScript; }; /** -* empty validator always returns true -*/ + * empty validator always returns true + */ template class CNullValidator : CValidatorBase { public: - CNullValidator(CC_info* cp) - : CValidatorBase(cp) { } - - virtual bool isVinValidator() const { return false; } - virtual bool validateVout(CTxOut vout, std::string& message) const { return true; } - virtual bool validateVin(CTxIn vin, CTxOut prevVout, std::string& message) const { return true; } + CNullValidator(CCcontract_info* cp) + : CValidatorBase(cp) { } + + virtual bool isVinValidator() const { return false; } + virtual bool validateVout(CTxOut vout, std::string& message) const { return true; } + virtual bool validateVin(CTxIn vin, CTxOut prevVout, std::string& message) const { return true; } }; diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 641d0a4d2..f7f663db2 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -45,14 +45,14 @@ bool TransactionSignatureCreator::CreateSig(std::vector& vchSig, key = *pprivKey; else if (!keystore || !keystore->GetKey(address, key)) return false; - + uint256 hash; try { hash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, consensusBranchId); } catch (logic_error ex) { return false; } - + if (scriptCode.IsPayToCryptoCondition()) { CC *cc = (CC *)extraData; @@ -75,9 +75,9 @@ bool TransactionSignatureCreator::CreateSig(std::vector& vchSig, return false; } } - + vchSig.push_back((unsigned char)nHashType); - + return true; } @@ -128,7 +128,7 @@ std::vector &GetCryptoConditions() static bool initialized = false; static std::vector vCC = std::vector(); CCcontract_info C; - + if (!initialized) { // this should initialize any desired auto-signed crypto-conditions @@ -140,7 +140,7 @@ bool GetCCByUnspendableAddress(struct CC_info *cp, char *addrstr) { std::vector &vCC = GetCryptoConditions(); bool found = false; - + for (int i = 0; i < vCC.size(); i++) { if (strcmp(addrstr, vCC[i].unspendableCCaddr) == 0) @@ -157,7 +157,7 @@ bool CCinitLite(struct CC_info *cp, uint8_t evalcode) { std::vector &vCC = GetCryptoConditions(); bool found = false; - + for (int i = 0; i < vCC.size(); i++) { if (vCC[i].evalcode == evalcode) @@ -172,7 +172,7 @@ bool CCinitLite(struct CC_info *cp, uint8_t evalcode) bool _Getscriptaddress(char *destaddr, const CScript &scriptPubKey) { - CTxDestination address; + CTxDestination address; txnouttype whichType; std::vector> vvch = std::vector>(); if (Solver(scriptPubKey, whichType, vvch) && vvch[0].size() == 20) @@ -199,10 +199,10 @@ static bool SignStepCC(const BaseSignatureCreator& creator, const CScript& scrip vector vPK; vector vParams = vector(); COptCCParams p; - + // get information to sign with CCcontract_info C; - + scriptPubKey.IsPayToCryptoCondition(&subScript, vParams); if (vParams.empty()) { @@ -219,12 +219,12 @@ static bool SignStepCC(const BaseSignatureCreator& creator, const CScript& scrip { p = COptCCParams(vParams[0]); } - + if (p.IsValid() && p.vKeys.size() >= p.n) { bool is1of2 = (p.m == 1 && p.n == 2); CKey privKey; - + // must be a valid cc eval code if (CCinitLite(&C, p.evalCode)) { @@ -232,7 +232,7 @@ static bool SignStepCC(const BaseSignatureCreator& creator, const CScript& scrip if (!is1of2) { bool havePriv = creator.KeyStore().GetKey(p.vKeys[0].GetID(), privKey); - + // if we don't have the private key, it must be the unspendable address if (!havePriv && (p.vKeys[0] == CPubKey(ParseHex(C.CChexstr)))) { @@ -240,9 +240,9 @@ static bool SignStepCC(const BaseSignatureCreator& creator, const CScript& scrip std::vector vch(&(C.CCpriv[0]), C.CCpriv + sizeof(C.CCpriv)); privKey.Set(vch.begin(), vch.end(), false); } - + CC *cc = CCcond1(p.evalCode, p.vKeys[0]); - + if (cc) { vector vch; @@ -254,7 +254,7 @@ static bool SignStepCC(const BaseSignatureCreator& creator, const CScript& scrip { fprintf(stderr,"vin has 1of1 CC signing error with address.(%s)\n", p.vKeys[0].GetID().ToString().c_str()); } - + cc_free(cc); return ret.size() != 0; } @@ -266,7 +266,7 @@ static bool SignStepCC(const BaseSignatureCreator& creator, const CScript& scrip { if (creator.IsKeystoreValid() && creator.KeyStore().GetKey(pk.GetID(), privKey) && privKey.IsValid()) break; - + if (pk == CPubKey(ParseHex(C.CChexstr))) { privKey = CKey(); @@ -275,12 +275,12 @@ static bool SignStepCC(const BaseSignatureCreator& creator, const CScript& scrip break; } } - + if (!privKey.IsValid()) return false; - + CC *cc = CCcond1of2(p.evalCode, p.vKeys[0], p.vKeys[1]); - + if (cc) { vector vch; @@ -292,7 +292,7 @@ static bool SignStepCC(const BaseSignatureCreator& creator, const CScript& scrip { fprintf(stderr,"vin has 1of2 CC signing error with addresses.(%s)\n(%s)\n", p.vKeys[0].GetID().ToString().c_str(), p.vKeys[1].GetID().ToString().c_str()); } - + cc_free(cc); return ret.size() != 0; } @@ -314,9 +314,9 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP CScript scriptRet; uint160 h160; ret.clear(); - + vector vSolutions; - + if (!Solver(scriptPubKey, whichTypeRet, vSolutions)) { // if this is a CLTV script, solve for the destination after CLTV @@ -324,10 +324,10 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP { uint8_t pushOp = scriptPubKey[0]; uint32_t scriptStart = pushOp + 3; - + // check post CLTV script CScript postfix = CScript(scriptPubKey.size() > scriptStart ? scriptPubKey.begin() + scriptStart : scriptPubKey.end(), scriptPubKey.end()); - + // check again with only postfix subscript if (!Solver(postfix, whichTypeRet, vSolutions)) return false; @@ -335,44 +335,44 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP else return false; } - + CKeyID keyID; - + switch (whichTypeRet) { - case TX_NONSTANDARD: - case TX_NULL_DATA: - return false; - case TX_PUBKEY: - keyID = CPubKey(vSolutions[0]).GetID(); - return Sign1(keyID, creator, scriptPubKey, ret, consensusBranchId); - case TX_PUBKEYHASH: - keyID = CKeyID(uint160(vSolutions[0])); - if (!Sign1(keyID, creator, scriptPubKey, ret, consensusBranchId)) + case TX_NONSTANDARD: + case TX_NULL_DATA: return false; - else - { - CPubKey vch; - creator.KeyStore().GetPubKey(keyID, vch); - ret.push_back(ToByteVector(vch)); - } - return true; - case TX_SCRIPTHASH: - if (creator.KeyStore().GetCScript(uint160(vSolutions[0]), scriptRet)) { - ret.push_back(std::vector(scriptRet.begin(), scriptRet.end())); + case TX_PUBKEY: + keyID = CPubKey(vSolutions[0]).GetID(); + return Sign1(keyID, creator, scriptPubKey, ret, consensusBranchId); + case TX_PUBKEYHASH: + keyID = CKeyID(uint160(vSolutions[0])); + if (!Sign1(keyID, creator, scriptPubKey, ret, consensusBranchId)) + return false; + else + { + CPubKey vch; + creator.KeyStore().GetPubKey(keyID, vch); + ret.push_back(ToByteVector(vch)); + } return true; - } - return false; - - case TX_CRYPTOCONDITION: - return SignStepCC(creator, scriptPubKey, vSolutions, ret, consensusBranchId); - - case TX_MULTISIG: - ret.push_back(valtype()); // workaround CHECKMULTISIG bug - return (SignN(vSolutions, creator, scriptPubKey, ret, consensusBranchId)); - - default: - return false; + case TX_SCRIPTHASH: + if (creator.KeyStore().GetCScript(uint160(vSolutions[0]), scriptRet)) { + ret.push_back(std::vector(scriptRet.begin(), scriptRet.end())); + return true; + } + return false; + + case TX_CRYPTOCONDITION: + return SignStepCC(creator, scriptPubKey, vSolutions, ret, consensusBranchId); + + case TX_MULTISIG: + ret.push_back(valtype()); // workaround CHECKMULTISIG bug + return (SignN(vSolutions, creator, scriptPubKey, ret, consensusBranchId)); + + default: + return false; } } @@ -399,7 +399,7 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu txnouttype whichType; solved = SignStep(creator, script, result, whichType, consensusBranchId); CScript subscript; - + if (solved && whichType == TX_SCRIPTHASH) { // Solver returns the subscript that needs to be evaluated; @@ -409,9 +409,9 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu solved = solved && SignStep(creator, script, result, whichType, consensusBranchId) && whichType != TX_SCRIPTHASH; result.push_back(std::vector(subscript.begin(), subscript.end())); } - + sigdata.scriptSig = PushAll(result); - + // Test solution return solved && VerifyScript(sigdata.scriptSig, fromPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker(), consensusBranchId); } @@ -431,19 +431,19 @@ void UpdateTransaction(CMutableTransaction& tx, unsigned int nIn, const Signatur } bool SignSignature( - const CKeyStore &keystore, - const CScript& fromPubKey, - CMutableTransaction& txTo, - unsigned int nIn, - const CAmount& amount, - int nHashType, - uint32_t consensusBranchId) + const CKeyStore &keystore, + const CScript& fromPubKey, + CMutableTransaction& txTo, + unsigned int nIn, + const CAmount& amount, + int nHashType, + uint32_t consensusBranchId) { assert(nIn < txTo.vin.size()); - + CTransaction txToConst(txTo); TransactionSignatureCreator creator(&keystore, &txToConst, nIn, amount, nHashType); - + SignatureData sigdata; bool ret = ProduceSignature(creator, fromPubKey, sigdata, consensusBranchId); UpdateTransaction(txTo, nIn, sigdata); @@ -451,24 +451,24 @@ bool SignSignature( } bool SignSignature( - const CKeyStore &keystore, - const CTransaction& txFrom, - CMutableTransaction& txTo, - unsigned int nIn, - int nHashType, - uint32_t consensusBranchId) + const CKeyStore &keystore, + const CTransaction& txFrom, + CMutableTransaction& txTo, + unsigned int nIn, + int nHashType, + uint32_t consensusBranchId) { assert(nIn < txTo.vin.size()); CTxIn& txin = txTo.vin[nIn]; assert(txin.prevout.n < txFrom.vout.size()); const CTxOut& txout = txFrom.vout[txin.prevout.n]; - + return SignSignature(keystore, txout.scriptPubKey, txTo, nIn, txout.nValue, nHashType, consensusBranchId); } static vector CombineMultisig(const CScript& scriptPubKey, const BaseSignatureChecker& checker, - const vector& vSolutions, - const vector& sigs1, const vector& sigs2, uint32_t consensusBranchId) + const vector& vSolutions, + const vector& sigs1, const vector& sigs2, uint32_t consensusBranchId) { // Combine all the signatures we've got: set allsigs; @@ -482,7 +482,7 @@ static vector CombineMultisig(const CScript& scriptPubKey, const BaseSi if (!v.empty()) allsigs.insert(v); } - + // Build a map of pubkey -> signature by matching sigs to pubkeys: assert(vSolutions.size() > 1); unsigned int nSigsRequired = vSolutions.front()[0]; @@ -495,7 +495,7 @@ static vector CombineMultisig(const CScript& scriptPubKey, const BaseSi const valtype& pubkey = vSolutions[i+1]; if (sigs.count(pubkey)) continue; // Already got a sig for this pubkey - + if (checker.CheckSig(sig, pubkey, scriptPubKey, consensusBranchId)) { sigs[pubkey] = sig; @@ -517,108 +517,108 @@ static vector CombineMultisig(const CScript& scriptPubKey, const BaseSi // Fill any missing with OP_0: for (unsigned int i = nSigsHave; i < nSigsRequired; i++) result.push_back(valtype()); - + return result; } namespace { -struct Stacks -{ - std::vector script; - - Stacks() {} - explicit Stacks(const std::vector& scriptSigStack_) : script(scriptSigStack_) {} - explicit Stacks(const SignatureData& data, uint32_t consensusBranchId) { - EvalScript(script, data.scriptSig, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker(), consensusBranchId); - } - - SignatureData Output() const { - SignatureData result; - result.scriptSig = PushAll(script); - return result; - } -}; + struct Stacks + { + std::vector script; + + Stacks() {} + explicit Stacks(const std::vector& scriptSigStack_) : script(scriptSigStack_) {} + explicit Stacks(const SignatureData& data, uint32_t consensusBranchId) { + EvalScript(script, data.scriptSig, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker(), consensusBranchId); + } + + SignatureData Output() const { + SignatureData result; + result.scriptSig = PushAll(script); + return result; + } + }; } static Stacks CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, - const txnouttype txType, const vector& vSolutions, - Stacks sigs1, Stacks sigs2, uint32_t consensusBranchId) + const txnouttype txType, const vector& vSolutions, + Stacks sigs1, Stacks sigs2, uint32_t consensusBranchId) { switch (txType) { - case TX_NONSTANDARD: - case TX_NULL_DATA: - // Don't know anything about this, assume bigger one is correct: - if (sigs1.script.size() >= sigs2.script.size()) - return sigs1; - return sigs2; - case TX_PUBKEY: - case TX_PUBKEYHASH: - case TX_CRYPTOCONDITION: - // Signatures are bigger than placeholders or empty scripts: - if (sigs1.script.empty() || sigs1.script[0].empty()) + case TX_NONSTANDARD: + case TX_NULL_DATA: + // Don't know anything about this, assume bigger one is correct: + if (sigs1.script.size() >= sigs2.script.size()) + return sigs1; return sigs2; - return sigs1; - case TX_SCRIPTHASH: - if (sigs1.script.empty() || sigs1.script.back().empty()) - return sigs2; - else if (sigs2.script.empty() || sigs2.script.back().empty()) + case TX_PUBKEY: + case TX_PUBKEYHASH: + case TX_CRYPTOCONDITION: + // Signatures are bigger than placeholders or empty scripts: + if (sigs1.script.empty() || sigs1.script[0].empty()) + return sigs2; return sigs1; - else - { - // Recur to combine: - valtype spk = sigs1.script.back(); - CScript pubKey2(spk.begin(), spk.end()); - - txnouttype txType2; - vector > vSolutions2; - Solver(pubKey2, txType2, vSolutions2); - sigs1.script.pop_back(); - sigs2.script.pop_back(); - Stacks result = CombineSignatures(pubKey2, checker, txType2, vSolutions2, sigs1, sigs2, consensusBranchId); - result.script.push_back(spk); - return result; - } - case TX_MULTISIG: - return Stacks(CombineMultisig(scriptPubKey, checker, vSolutions, sigs1.script, sigs2.script, consensusBranchId)); - default: - return Stacks(); + case TX_SCRIPTHASH: + if (sigs1.script.empty() || sigs1.script.back().empty()) + return sigs2; + else if (sigs2.script.empty() || sigs2.script.back().empty()) + return sigs1; + else + { + // Recur to combine: + valtype spk = sigs1.script.back(); + CScript pubKey2(spk.begin(), spk.end()); + + txnouttype txType2; + vector > vSolutions2; + Solver(pubKey2, txType2, vSolutions2); + sigs1.script.pop_back(); + sigs2.script.pop_back(); + Stacks result = CombineSignatures(pubKey2, checker, txType2, vSolutions2, sigs1, sigs2, consensusBranchId); + result.script.push_back(spk); + return result; + } + case TX_MULTISIG: + return Stacks(CombineMultisig(scriptPubKey, checker, vSolutions, sigs1.script, sigs2.script, consensusBranchId)); + default: + return Stacks(); } } SignatureData CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, - const SignatureData& scriptSig1, const SignatureData& scriptSig2, - uint32_t consensusBranchId) + const SignatureData& scriptSig1, const SignatureData& scriptSig2, + uint32_t consensusBranchId) { txnouttype txType; vector > vSolutions; Solver(scriptPubKey, txType, vSolutions); - + return CombineSignatures( - scriptPubKey, checker, txType, vSolutions, - Stacks(scriptSig1, consensusBranchId), - Stacks(scriptSig2, consensusBranchId), - consensusBranchId).Output(); + scriptPubKey, checker, txType, vSolutions, + Stacks(scriptSig1, consensusBranchId), + Stacks(scriptSig2, consensusBranchId), + consensusBranchId).Output(); } namespace { -/** Dummy signature checker which accepts all signatures. */ -class DummySignatureChecker : public BaseSignatureChecker -{ -public: - DummySignatureChecker() {} - - bool CheckSig( - const std::vector& scriptSig, - const std::vector& vchPubKey, - const CScript& scriptCode, - uint32_t consensusBranchId) const + /** Dummy signature checker which accepts all signatures. */ + class DummySignatureChecker : public BaseSignatureChecker { - return true; - } -}; -const DummySignatureChecker dummyChecker; + public: + DummySignatureChecker() {} + + bool CheckSig( + const std::vector& scriptSig, + const std::vector& vchPubKey, + const CScript& scriptCode, + uint32_t consensusBranchId) const + { + return true; + } + }; + const DummySignatureChecker dummyChecker; } const BaseSignatureChecker& DummySignatureCreator::Checker() const @@ -627,12 +627,12 @@ const BaseSignatureChecker& DummySignatureCreator::Checker() const } bool DummySignatureCreator::CreateSig( - std::vector& vchSig, - const CKeyID& keyid, - const CScript& scriptCode, - uint32_t consensusBranchId, - CKey *key, - void *extraData) const + std::vector& vchSig, + const CKeyID& keyid, + const CScript& scriptCode, + uint32_t consensusBranchId, + CKey *key, + void *extraData) const { // Create a dummy signature that is a valid DER-encoding vchSig.assign(72, '\000'); @@ -647,3 +647,4 @@ bool DummySignatureCreator::CreateSig( vchSig[6 + 33 + 32] = SIGHASH_ALL; return true; } +