diff --git a/src/cc/CCinclude.h b/src/cc/CCinclude.h index 0dc7f446d..4b6b437b4 100644 --- a/src/cc/CCinclude.h +++ b/src/cc/CCinclude.h @@ -191,6 +191,8 @@ CTxOut MakeTokensCC1of2vout(uint8_t evalcode, CAmount nValue, CPubKey pk1, CPubK CTxOut MakeTokensCC1vout(uint8_t evalcode, CAmount nValue, CPubKey pk); CC *MakeTokensCCcond1of2(uint8_t evalcode, CPubKey pk1, CPubKey pk2); CC *MakeTokensCCcond1(uint8_t evalcode, CPubKey pk); + +bool GetTokensCCaddress(struct CCcontract_info *cp, char *destaddr, CPubKey pk); bool GetTokensCCaddress1of2(struct CCcontract_info *cp, char *destaddr, CPubKey pk, CPubKey pk2); void CCaddrTokens1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, char *coinaddr); diff --git a/src/cc/CCtokens.cpp b/src/cc/CCtokens.cpp index 2f9a50a93..0bd4dd991 100644 --- a/src/cc/CCtokens.cpp +++ b/src/cc/CCtokens.cpp @@ -201,10 +201,10 @@ bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction & //vout.0: issuance tokenoshis to CC //vout.1: normal output for change (if any) //vout.n-1: opreturn EVAL_TOKENS 'c' - if (evalCodeInOpret != EVAL_TOKENS) - return eval->Invalid("unexpected TokenValidate for createtoken"); - else - return true; + //if (evalCodeInOpret != EVAL_TOKENS) + // return eval->Invalid("unexpected TokenValidate for createtoken"); + //else + return true; case 't': // transfer //vin.0: normal input @@ -225,15 +225,15 @@ bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction & // forward validation if evalcode in opret is not EVAL_TOKENS // init for forwarding validation call - if (evalCodeInOpret != EVAL_TOKENS) { // TODO: should we check also only allowed for tokens evalcodes, like EVAL_ASSETS, EVAL_GATEWAYS? - struct CCcontract_info *cpOther = NULL, C; + //if (evalCodeInOpret != EVAL_TOKENS) { // TODO: should we check also only allowed for tokens evalcodes, like EVAL_ASSETS, EVAL_GATEWAYS? + // struct CCcontract_info *cpOther = NULL, C; - cpOther = CCinit(&C, evalCodeInOpret); - if (cpOther) - return cpOther->validate(cpOther, eval, tx, nIn); - else - return eval->Invalid("unsupported evalcode in opret"); - } + // cpOther = CCinit(&C, evalCodeInOpret); + // if (cpOther) + // return cpOther->validate(cpOther, eval, tx, nIn); + // else + // return eval->Invalid("unsupported evalcode in opret"); + //} return true; // what does this do? // return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts)); @@ -285,17 +285,18 @@ bool ExtractVinPubkeys(struct CCcontract_info *cp, CTransaction tx, std::vector< thread_local uint32_t tokenValIndentSize = 0; // validates opret for token tx: -bool ValidateTokenOpret(CTransaction tx, int32_t v, uint256 tokenid, uint8_t &evalCodeInOpret, std::vector &voutPubkeys, std::vector &vopretExtra) { +bool ValidateTokenOpret(CTransaction tx, int32_t v, uint256 tokenid, std::vector &voutPubkeys, std::vector &vopretExtra) { uint256 tokenidOpret, tokenidOpret2; uint8_t funcid; + uint8_t dummyEvalCode; // this is just for log messages indentation fur debugging recursive calls: std::string indentStr = std::string().append(tokenValIndentSize, '.'); int32_t n = tx.vout.size(); - if ((funcid = DecodeTokenOpRet(tx.vout[n - 1].scriptPubKey, evalCodeInOpret, tokenidOpret, voutPubkeys, vopretExtra)) == 0) + if ((funcid = DecodeTokenOpRet(tx.vout[n - 1].scriptPubKey, dummyEvalCode, tokenidOpret, voutPubkeys, vopretExtra)) == 0) { std::cerr << indentStr << "ValidateTokenOpret() DecodeTokenOpret could not parse opret for txid=" << tx.GetHash().GetHex() << std::endl; return(false); @@ -364,14 +365,24 @@ int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *c // moved opret checking to this new reusable func (dimxy): std::vector voutPubkeys; - uint8_t evalCodeInOpret = 0; - const bool valOpret = ValidateTokenOpret(tx, v, reftokenid, evalCodeInOpret, voutPubkeys, vopretExtra); + const bool valOpret = ValidateTokenOpret(tx, v, reftokenid, voutPubkeys, vopretExtra); //std::cerr << indentStr << "IsTokensvout() ValidateTokenOpret returned=" << std::boolalpha << valOpret << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; if (valOpret) { //std::cerr << indentStr << "IsTokensvout() ValidateTokenOpret returned true" << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; if (checkPubkeys) { // verify that the vout is within EVAL_TOKENS: + CScript contractScript = CScript(vopretExtra); + std::vector vcontractOpret; + + GetOpReturnData(contractScript, vcontractOpret); + if (vcontractOpret.size() == 0) { + std::cerr << "IsTokensvout() empty contract opret" << std::endl; + return 0; + } + + uint8_t evalCodeInOpret = vcontractOpret.begin()[0]; + if (voutPubkeys.size() >= 1 && voutPubkeys.size() <= 2) { CTxOut testVout; if (voutPubkeys.size() == 1) diff --git a/src/cc/CCtx.cpp b/src/cc/CCtx.cpp index 30c4c1e62..d85f59473 100644 --- a/src/cc/CCtx.cpp +++ b/src/cc/CCtx.cpp @@ -43,9 +43,9 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); CTransaction vintx; std::string hex; uint256 hashBlock; uint64_t mask=0,nmask=0,vinimask=0; int64_t utxovalues[CC_MAXVINS],change,normalinputs=0,totaloutputs=0,normaloutputs=0,totalinputs=0,normalvins=0,ccvins=0; - int32_t i,utxovout,n,err = 0; char myaddr[64],destaddr[64],unspendable[64]; + int32_t i,utxovout,n,err = 0; char myaddr[64],destaddr[64],unspendable[64],mytokensaddr[64]; uint8_t *privkey,myprivkey[32],unspendablepriv[32],*msg32 = 0; - CC *mycond=0, *othercond=0, *othercond2=0, *othercond3=0, *othercond1of2=NULL, *othercond1of2tokens = NULL, *cond; + CC *mycond=0, *othercond=0, *othercond2=0, *othercond3=0, *othercond1of2=NULL, *othercond1of2tokens = NULL, *cond, *mytokenscond = NULL; CPubKey unspendablepk; n = mtx.vout.size(); @@ -61,11 +61,17 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran return("0"); } Myprivkey(myprivkey); - unspendablepk = GetUnspendable(cp,unspendablepriv); + GetCCaddress(cp,myaddr,mypk); mycond = MakeCCcond1(cp->evalcode,mypk); + + GetTokensCCaddress(cp, myaddr, mypk); + mytokenscond = MakeTokensCCcond1(cp->evalcode, mypk); + + unspendablepk = GetUnspendable(cp,unspendablepriv); GetCCaddress(cp,unspendable,unspendablepk); - othercond = MakeCCcond1(cp->evalcode,unspendablepk); + othercond = MakeCCcond1(cp->evalcode,unspendablepk); + //Reorder vins so that for multiple normal vins all other except vin0 goes to the end //This is a must to avoid hardfork change of validation in every CC, because there could be maximum one normal vin at the begining with current validation. for (i=0; icoins1of2addr, destaddr) == 0) + else if (strcmp(cp->coins1of2addr, destaddr) == 0) { fprintf(stderr,"FinalizeCCTx() matched %s unspendable1of2!\n",cp->coins1of2addr); privkey = myprivkey; diff --git a/src/cc/CCutils.cpp b/src/cc/CCutils.cpp index 82b74cc11..6d2d4a037 100644 --- a/src/cc/CCutils.cpp +++ b/src/cc/CCutils.cpp @@ -94,8 +94,8 @@ CC *MakeTokensCCcond1(uint8_t evalcode, CPubKey pk) { std::vector pks; pks.push_back(CCNewSecp256k1(pk)); - CC *condEvalCC = CCNewEval(E_MARSHAL(ss << evalcode)); - CC *condEvalTokensCC = CCNewEval(E_MARSHAL(ss << (uint8_t)EVAL_TOKENS)); // this is eval token cc + CC *condEvalCC = CCNewEval(E_MARSHAL(ss << evalcode)); // add eval cc + CC *condEvalTokensCC = CCNewEval(E_MARSHAL(ss << (uint8_t)EVAL_TOKENS)); // add also eval token cc CC *Sig = CCNewThreshold(1, pks); return CCNewThreshold(3, { condEvalCC, condEvalTokensCC, Sig }); } @@ -345,6 +345,27 @@ bool GetCCaddress(struct CCcontract_info *cp,char *destaddr,CPubKey pk) return(_GetCCaddress(destaddr,cp->evalcode,pk)); } +bool _GetTokensCCaddress(char *destaddr, uint8_t evalcode, CPubKey pk) +{ + CC *payoutCond; + destaddr[0] = 0; + if ((payoutCond = MakeTokensCCcond1(evalcode, pk)) != 0) + { + Getscriptaddress(destaddr, CCPubKey(payoutCond)); + cc_free(payoutCond); + } + return(destaddr[0] != 0); +} + +bool GetTokensCCaddress(struct CCcontract_info *cp, char *destaddr, CPubKey pk) +{ + destaddr[0] = 0; + if (pk.size() == 0) + pk = GetUnspendable(cp, 0); + return(_GetTokensCCaddress(destaddr, cp->evalcode, pk)); +} + + bool GetCCaddress1of2(struct CCcontract_info *cp,char *destaddr,CPubKey pk,CPubKey pk2) { CC *payoutCond; diff --git a/src/cc/heir.cpp b/src/cc/heir.cpp index 7d1d56006..784e0a88c 100644 --- a/src/cc/heir.cpp +++ b/src/cc/heir.cpp @@ -122,23 +122,27 @@ bool HeirValidate(struct CCcontract_info* cpHeir, Eval* eval, const CTransaction //if (chainActive.Height() < 741) // return true; - uint8_t funcId; uint256 fundingTxidInOpret = zeroid, latestTxid = zeroid, dummyTokenid, tokenid = zeroid; CScript fundingTxOpRetScript; uint8_t hasHeirSpendingBegun = 0, dummyHasHeirSpendingBegun; - int32_t heirType = NOT_HEIR; - funcId = DecodeHeirOpRet(tx.vout[numvouts - 1].scriptPubKey, dummyTokenid, fundingTxidInOpret, dummyHasHeirSpendingBegun, true); - if(funcId != 0) - heirType = HEIR_COINS; - else { - funcId = DecodeHeirOpRet(tx.vout[numvouts - 1].scriptPubKey, tokenid, fundingTxidInOpret, dummyHasHeirSpendingBegun, false); - if (funcId != 0) - heirType = HEIR_TOKENS; + + uint8_t evalCodeTokens = 0; + std::vector voutPubkeys; + std::vector vopretExtra; + + CScript heirScript = tx.vout[tx.vout.size() - 1].scriptPubKey; + int32_t heirType = HEIR_COINS; + + uint8_t funcId = DecodeTokenOpRet(heirScript, evalCodeTokens, tokenid, voutPubkeys, vopretExtra); + if (funcId != 0) { + heirScript = CScript(vopretExtra); + heirType = HEIR_TOKENS; } + funcId = DecodeHeirOpRet(heirScript, fundingTxidInOpret, dummyHasHeirSpendingBegun, true); - if (heirType == NOT_HEIR) + if (funcId == 0) return eval->Invalid("invalid opreturn format"); if (funcId != 'F') { @@ -248,11 +252,11 @@ bool HeirValidate(struct CCcontract_info* cpHeir, Eval* eval, const CTransaction * Checks if vout is to cryptocondition address * @return vout value in satoshis */ -int64_t IsHeirFundingVout(struct CCcontract_info* cp, const CTransaction& tx, int32_t voutIndex, CPubKey ownerPubkey, CPubKey heirPubkey) +template int64_t IsHeirFundingVout(struct CCcontract_info* cp, const CTransaction& tx, int32_t voutIndex, CPubKey ownerPubkey, CPubKey heirPubkey) { char destaddr[65], heirFundingAddr[65]; - GetTokensCCaddress1of2(cp, heirFundingAddr, ownerPubkey, heirPubkey); + Helper::GetCoinsOrTokensCCaddress1of2(cp, heirFundingAddr, ownerPubkey, heirPubkey); if (tx.vout[voutIndex].scriptPubKey.IsPayToCryptoCondition() != 0) { // NOTE: dimxy it was unsafe 'Getscriptaddress(destaddr,tx.vout[voutIndex].scriptPubKey) > 0' here: if (Getscriptaddress(destaddr, tx.vout[voutIndex].scriptPubKey) && strcmp(destaddr, heirFundingAddr) == 0) @@ -317,6 +321,8 @@ CScript EncodeHeirOpRet(uint8_t funcid, uint256 fundingtxid, uint8_t hasHeirSpe fundingtxid = revuint256(fundingtxid); return CScript() << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << fundingtxid << hasHeirSpendingBegun); } + +/* // makes opret for tokens while they are inside Heir contract address space - initial funding CScript EncodeHeirTokensCreateOpRet(uint8_t heirFuncId, uint256 tokenid, std::vector voutPubkeys, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string hearName) { @@ -348,10 +354,11 @@ CScript EncodeHeirTokensOpRet(uint8_t heirFuncId, uint256 tokenid, std::vector vopretExtra, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, uint256& fundingTxidInOpret, uint8_t &hasHeirSpendingBegun) { +/*uint8_t _UnmarshalOpret(std::vector vopretExtra, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, uint256& fundingTxidInOpret, uint8_t &hasHeirSpendingBegun) { uint8_t heirFuncId = 0; hasHeirSpendingBegun = 0; @@ -363,70 +370,66 @@ uint8_t _UnmarshalOpret(std::vector vopretExtra, CPubKey& ownerPubkey, } \ }); - if (!result /*|| assetFuncId != 't' -- any tx is ok*/) + if (!result )// || assetFuncId != 't' -- any tx is ok) return (uint8_t)0; return heirFuncId; -} +}*/ + /** * decode opret vout for Heir contract */ -template uint8_t _DecodeHeirOpRet(CScript scriptPubKey, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, uint256& fundingTxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging) +uint8_t _DecodeHeirOpRet(CScript scriptPubKey, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, uint256& fundingTxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging) { - - std::vector vopretExtra; + std::vector vopret; uint8_t evalCodeInOpret = 0; - uint256 dummyTokenid; - std::vector voutPubkeysDummy; + uint8_t heirFuncId = 0; fundingTxidInOpret = zeroid; //to init - tokenid = zeroid; - - if (typeid(Helper) == typeid(TokenHelper)) { // if caller thinks it is a token - - // First - decode token opret: - uint8_t tokenFuncId = DecodeTokenOpRet(scriptPubKey, evalCodeInOpret, tokenid, voutPubkeysDummy, vopretExtra); - if (tokenFuncId == 0) { - if (!noLogging) std::cerr << "DecodeHeirOpRet() warning: not heir token opret, tokenFuncId=" << (int)tokenFuncId << std::endl; - return (uint8_t)0; - } + GetOpReturnData(scriptPubKey, vopret); + if (vopret.size() == 0) { + if (!noLogging) std::cerr << "_DecodeHeirOpRet() warning: empty opret" << std::endl; + return (uint8_t)0; } - else { - std::vector vopret; - - GetOpReturnData(scriptPubKey, vopret); - if (vopret.size() == 0) { - if (!noLogging) std::cerr << "DecodeHeirOpRet() warning: empty opret" << std::endl; - return (uint8_t)0; - } - evalCodeInOpret = vopret.begin()[0]; - vopretExtra = std::vector( vopret.begin()+1, vopret.end() ); // vopretExtra = vopret + 1, get it for futher parsing - } - - if (vopretExtra.size() > 1 && evalCodeInOpret == EVAL_HEIR) { + evalCodeInOpret = vopret.begin()[0]; + + if (vopret.size() > 1 && evalCodeInOpret == EVAL_HEIR) { // NOTE: it unmarshals for all F, A and C - uint8_t heirFuncId = _UnmarshalOpret(vopretExtra, ownerPubkey, heirPubkey, inactivityTime, heirName, fundingTxidInOpret, hasHeirSpendingBegun); - - /*std::cerr << "DecodeHeirOpRet()" + uint8_t heirFuncId = 0; + hasHeirSpendingBegun = 0; + + bool result = E_UNMARSHAL(vopret, { ss >> evalCodeInOpret; ss >> heirFuncId; \ + if (heirFuncId == 'F') { \ + ss >> ownerPubkey; ss >> heirPubkey; ss >> inactivityTime; ss >> heirName; \ + } \ + else { \ + ss >> fundingTxidInOpret >> hasHeirSpendingBegun; \ + } \ + }); + + if (!result) { + if (!noLogging) std::cerr << "_DecodeHeirOpRet() could not unmarshal opret, evalCode=" << (int)evalCodeInOpret << std::endl; + return (uint8_t)0; + } + + /* std::cerr << "DecodeHeirOpRet()" << " heirFuncId=" << (char)(heirFuncId ? heirFuncId : ' ') << " ownerPubkey=" << HexStr(ownerPubkey) << " heirPubkey=" << HexStr(heirPubkey) << " heirName=" << heirName << " inactivityTime=" << inactivityTime - << " hasHeirSpendingBegun=" << (int)hasHeirSpendingBegun << std::endl;*/ + << " hasHeirSpendingBegun=" << (int)hasHeirSpendingBegun << std::endl; */ - - //if (e == EVAL_HEIR && IS_CHARINSTR(funcId, "FAC")) - if (Helper::isMyFuncId(heirFuncId)) { + if (isMyFuncId(heirFuncId)) { fundingTxidInOpret = revuint256(fundingTxidInOpret); return heirFuncId; } else { - if(!noLogging) std::cerr << "DecodeHeirOpRet() error: unexpected opret, heirFuncId=" << (char)(heirFuncId ? heirFuncId : ' ') << std::endl; + if(!noLogging) std::cerr << "_DecodeHeirOpRet() unexpected opret type, heirFuncId=" << (char)(heirFuncId ? heirFuncId : ' ') << std::endl; } } else { - if (!noLogging) std::cerr << "DecodeHeirOpRet() error: not a heir opret, vopretExtra.size() == 0 or not EVAL_HEIR evalcode=" << (int)evalCodeInOpret << std::endl; + if (!noLogging) std::cerr << "_DecodeHeirOpRet() not a heir opret, vopretExtra.size() == 0 or not EVAL_HEIR evalcode=" << (int)evalCodeInOpret << std::endl; } return (uint8_t)0; } @@ -434,24 +437,24 @@ template uint8_t _DecodeHeirOpRet(CScript scriptPubKey, uint256 & /** * overload for 'F' opret */ -template uint8_t DecodeHeirOpRet(CScript scriptPubKey, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, bool noLogging) +uint8_t DecodeHeirOpRet(CScript scriptPubKey, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, bool noLogging) { uint256 dummytxid; uint8_t dummyHasHeirSpendingBegun; - return _DecodeHeirOpRet(scriptPubKey, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, dummytxid, dummyHasHeirSpendingBegun, noLogging); + return _DecodeHeirOpRet(scriptPubKey, ownerPubkey, heirPubkey, inactivityTime, heirName, dummytxid, dummyHasHeirSpendingBegun, noLogging); } /** * overload for A, C oprets and AddHeirContractInputs */ -template uint8_t DecodeHeirOpRet(CScript scriptPubKey, uint256 &tokenid, uint256& fundingtxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging) +uint8_t DecodeHeirOpRet(CScript scriptPubKey, uint256& fundingtxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging) { CPubKey dummyOwnerPubkey, dummyHeirPubkey; int64_t dummyInactivityTime; std::string dummyHeirName; - return _DecodeHeirOpRet(scriptPubKey, tokenid, dummyOwnerPubkey, dummyHeirPubkey, dummyInactivityTime, dummyHeirName, fundingtxidInOpret, hasHeirSpendingBegun, noLogging); + return _DecodeHeirOpRet(scriptPubKey, dummyOwnerPubkey, dummyHeirPubkey, dummyInactivityTime, dummyHeirName, fundingtxidInOpret, hasHeirSpendingBegun, noLogging); } @@ -476,9 +479,22 @@ template uint256 _FindLatestFundingTx(uint256 fundingtxid, uint8_ // get initial funding tx and set it as initial lasttx: if (myGetTransaction(fundingtxid, fundingtx, hashBlock) && fundingtx.vout.size()) { uint256 dummytxid; + uint8_t evalCodeTokens = 0; + std::vector vopretExtra; + std::vector dummyVoutPubkeys; - // set ownerPubkey and heirPubkey: - if ((funcId = DecodeHeirOpRet(fundingtx.vout[fundingtx.vout.size() - 1].scriptPubKey, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName)) != 0) { + CScript heirScript = fundingtx.vout[fundingtx.vout.size() - 1].scriptPubKey; + if (typeid(Helper) == typeid(TokenHelper)) { + if (DecodeTokenOpRet(heirScript, evalCodeTokens, tokenid, dummyVoutPubkeys, vopretExtra) != 0) { + heirScript = CScript(vopretExtra); + } + else { + std::cerr << "FindLatestFundingTx() could not decode token opreturn for fundingtxid=" << fundingtxid.GetHex() << '\n'; + return zeroid; + } + } + funcId = DecodeHeirOpRet(heirScript, ownerPubkey, heirPubkey, inactivityTime, heirName); + if (funcId != 0) { // found at least funding tx! //std::cerr << "FindLatestFundingTx() lasttx currently is fundingtx, txid=" << fundingtxid.GetHex() << " opreturn type=" << (char)funcId << '\n'; fundingOpretScript = fundingtx.vout[fundingtx.vout.size() - 1].scriptPubKey; @@ -493,7 +509,7 @@ template uint256 _FindLatestFundingTx(uint256 fundingtxid, uint8_ std::vector> unspentOutputs; struct CCcontract_info *cp, C; - cp = CCinit(&C, Helper::getMyEval()); + cp = CCinit(&C, EVAL_HEIR); char coinaddr[64]; GetCCaddress1of2(cp, coinaddr, ownerPubkey, heirPubkey); // get the address of cryptocondition '1 of 2 pubkeys' @@ -531,9 +547,23 @@ template uint256 _FindLatestFundingTx(uint256 fundingtxid, uint8_ uint8_t tmpFuncId; uint8_t tmphasHeirSpendingBegun; - if (regtx.vout.size() > 0 && - (tmpFuncId = DecodeHeirOpRet(regtx.vout[regtx.vout.size() - 1].scriptPubKey, dummyTokenid, fundingTxidInOpret, tmphasHeirSpendingBegun, true)) != 0 && - fundingtxid == fundingTxidInOpret) { + uint8_t evalCodeTokens = 0; + std::vector vopretExtra; + std::vector dummyVoutPubkeys; + + CScript heirScript = regtx.vout[regtx.vout.size() - 1].scriptPubKey; + if (typeid(Helper) == typeid(TokenHelper)) { + if (DecodeTokenOpRet(heirScript, evalCodeTokens, tokenid, dummyVoutPubkeys, vopretExtra) != 0) { + heirScript = CScript(vopretExtra); + } + else { + std::cerr << "FindLatestFundingTx() could not decode token opreturn for fundingtxid=" << fundingtxid.GetHex() << '\n'; + return zeroid; + } + } + tmpFuncId = DecodeHeirOpRet(heirScript, fundingTxidInOpret, tmphasHeirSpendingBegun, true); + + if (regtx.vout.size() > 0 && tmpFuncId != 0 && fundingtxid == fundingTxidInOpret) { if (blockHeight > maxBlockHeight) { maxBlockHeight = blockHeight; @@ -581,12 +611,13 @@ template int64_t Add1of2AddressInputs(struct CCcontract_info* cp, std::vector> unspentOutputs; char coinaddr[64]; - GetTokensCCaddress1of2(cp, coinaddr, ownerPubkey, heirPubkey); // get address of cryptocondition '1 pubkey of 2 pubkeys' + Helper::GetCoinsOrTokensCCaddress1of2(cp, coinaddr, ownerPubkey, heirPubkey); // get address of cryptocondition '1 of 2 pubkeys' SetCCunspents(unspentOutputs, coinaddr); // char markeraddr[64]; // CCtxidaddr(markeraddr, fundingtxid); // SetCCunspents(unspentOutputs, markeraddr); + std::cerr << "Add1of2AddressInputs() using 1of2addr=" << coinaddr << " unspentOutputs.size()=" << unspentOutputs.size() << std::endl; for (std::vector>::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) { @@ -602,13 +633,22 @@ template int64_t Add1of2AddressInputs(struct CCcontract_info* cp, uint256 tokenid; uint256 fundingTxidInOpret; uint8_t dummyHasHeirSpendingBegun; + uint8_t evalCodeTokens = 0; + std::vector vopretExtra; + std::vector voutPubkeys; - uint8_t funcId = DecodeHeirOpRet(heirtx.vout[heirtx.vout.size() - 1].scriptPubKey, tokenid, fundingTxidInOpret, dummyHasHeirSpendingBegun, true); + CScript heirScript = heirtx.vout[heirtx.vout.size() - 1].scriptPubKey; + uint8_t funcId = DecodeTokenOpRet(heirScript, evalCodeTokens, tokenid, voutPubkeys, vopretExtra); + if (funcId != 0) { + heirScript = CScript(vopretExtra); + } + funcId = DecodeHeirOpRet(heirScript, fundingTxidInOpret, dummyHasHeirSpendingBegun, true); + if ((txid == fundingtxid || fundingTxidInOpret == fundingtxid) && funcId != 0 && - Helper::isMyFuncId(funcId) && + isMyFuncId(funcId) && // (typeid(Helper) == typeid(TokenHelper) && IsHeirvout(true, cp, nullptr, tokenid, vintx, voutIndex) > 0) && // deep validation for tokens - not used anymore - (voutValue = IsHeirFundingVout(cp, heirtx, voutIndex, ownerPubkey, heirPubkey)) > 0 && + (voutValue = IsHeirFundingVout(cp, heirtx, voutIndex, ownerPubkey, heirPubkey)) > 0 && !myIsutxo_spentinmempool(txid, voutIndex)) { std::cerr << "Add1of2AddressInputs() voutValue=" << voutValue << " satoshis=" << it->second.satoshis << '\n'; @@ -631,7 +671,7 @@ template int64_t Add1of2AddressInputs(struct CCcontract_info* cp, template int64_t LifetimeHeirContractFunds(struct CCcontract_info* cp, uint256 fundingtxid, CPubKey ownerPubkey, CPubKey heirPubkey) { char coinaddr[64]; - GetCCaddress1of2(cp, coinaddr, ownerPubkey, heirPubkey); // get the address of cryptocondition '1 of 2 pubkeys' + Helper::GetCoinsOrTokensCCaddress1of2(cp, coinaddr, ownerPubkey, heirPubkey); // get the address of cryptocondition '1 of 2 pubkeys' std::vector> addressIndexes; SetCCtxids(addressIndexes, coinaddr); @@ -644,17 +684,24 @@ template int64_t LifetimeHeirContractFunds(struct CCcontract_info CTransaction tx; if (GetTransaction(txid, tx, hashBlock, false) && tx.vout.size() > 0) { - uint8_t funcId; + uint8_t evalCodeTokens = 0; uint256 tokenid; uint256 fundingTxidInOpret; - const int32_t ivout = 0; + std::vector vopretExtra; + std::vector voutPubkeys; uint8_t dummyHasHeirSpendingBegun; - - funcId = DecodeHeirOpRet(tx.vout[tx.vout.size() - 1].scriptPubKey, tokenid, fundingTxidInOpret, dummyHasHeirSpendingBegun, true); + const int32_t ivout = 0; + + CScript heirScript = tx.vout[tx.vout.size() - 1].scriptPubKey; + uint8_t funcId = DecodeTokenOpRet(heirScript, evalCodeTokens, tokenid, voutPubkeys, vopretExtra); + if (funcId != 0) { + heirScript = CScript(vopretExtra); + } + funcId = DecodeHeirOpRet(heirScript, fundingTxidInOpret, dummyHasHeirSpendingBegun, true); //std::cerr << "LifetimeHeirContractFunds() found tx=" << txid.GetHex() << " vout[0].nValue=" << subtx.vout[ccVoutIdx].nValue << " opreturn=" << (char)funcId << '\n'; - if (funcId != 0 && (txid == fundingtxid || fundingTxidInOpret == fundingtxid) && Helper::isMyFuncId(funcId) && !Helper::isSpendingTx(funcId) + if (funcId != 0 && (txid == fundingtxid || fundingTxidInOpret == fundingtxid) && isMyFuncId(funcId) && !isSpendingTx(funcId) /* && !myIsutxo_spentinmempool(txid, ccVoutIdx) */) // include also tx in mempool { total += it->second; // dont do this: tx.vout[ivout].nValue; // in vin[0] always is the pay to 1of2 addr (funding or change) @@ -678,7 +725,7 @@ template std::string HeirFund(uint64_t txfee, int64_t amount, CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); struct CCcontract_info *cp, C; - cp = CCinit(&C, Helper::getMyEval()); + cp = CCinit(&C, EVAL_HEIR); if (txfee == 0) txfee = 10000; @@ -719,7 +766,6 @@ template std::string HeirFund(uint64_t txfee, int64_t amount, // add change for txfee and opreturn vouts and sign tx: return (FinalizeCCTx(0, cp, mtx, myPubkey, txfee, - // CScript() << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_HEIR << (uint8_t)'F' << myPubkey << heirPubkey << inactivityTimeSec << heirName))); Helper::makeCreateOpRet(tokenid, voutTokenPubkeys, myPubkey, heirPubkey, inactivityTimeSec, heirName))); } else // TODO: need result return unification with heiradd and claim @@ -759,7 +805,7 @@ template UniValue HeirAdd(uint256 fundingtxid, uint64_t txfee, in uint8_t funcId; uint8_t hasHeirSpendingBegun = 0; - cp = CCinit(&C, Helper::getMyEval()); + cp = CCinit(&C, EVAL_HEIR); if (txfee == 0) txfee = 10000; @@ -790,7 +836,7 @@ template UniValue HeirAdd(uint256 fundingtxid, uint64_t txfee, in std::cerr << "HeirAdd() adding markeraddr=" << markeraddr << '\n'; */ // add cryptocondition to spend this funded amount for either pk - mtx.vout.push_back(MakeCC1of2vout(Helper::getMyEval(), amount, ownerPubkey, heirPubkey)); // using always pubkeys from OP_RETURN in order to not mixing them up! + mtx.vout.push_back(Helper::make1of2Vout(amount, ownerPubkey, heirPubkey)); // using always pubkeys from OP_RETURN in order to not mixing them up! if (inputs > amount) change = (inputs - amount); // -txfee <-- txfee pays user @@ -801,7 +847,7 @@ template UniValue HeirAdd(uint256 fundingtxid, uint64_t txfee, in mtx.vout.push_back(Helper::makeUserVout(change, myPubkey)); } - // add 1of2 vout validation pubkeys: + // add 1of2 vout validation pubkeys - needed only for tokens: std::vector voutTokenPubkeys; voutTokenPubkeys.push_back(ownerPubkey); voutTokenPubkeys.push_back(heirPubkey); @@ -863,8 +909,6 @@ template UniValue HeirClaim(uint256 fundingtxid, uint64_t txfee std::string heirName; uint8_t hasHeirSpendingBegun = 0; - - //cp = CCinit(&C, Helper::getMyEval()); cp = CCinit(&C, EVAL_HEIR); if (txfee == 0) txfee = 10000; @@ -924,7 +968,7 @@ template UniValue HeirClaim(uint256 fundingtxid, uint64_t txfee // change to 1of2 funding addr: if (change != 0) { // vout[1] - mtx.vout.push_back(MakeCC1of2vout(Helper::getMyEval(), change, ownerPubkey, heirPubkey)); // using always pubkeys from OP_RETURN in order to not mixing them up! + mtx.vout.push_back(Helper::make1of2Vout(change, ownerPubkey, heirPubkey)); // using always pubkeys from OP_RETURN in order to not mixing them up! } // add marker vout: @@ -937,7 +981,7 @@ template UniValue HeirClaim(uint256 fundingtxid, uint64_t txfee uint8_t myprivkey[32]; char coinaddr[64]; // set priv key addresses in CC structure: - GetTokensCCaddress1of2(cp, coinaddr, ownerPubkey, heirPubkey); + Helper::GetCoinsOrTokensCCaddress1of2(cp, coinaddr, ownerPubkey, heirPubkey); Myprivkey(myprivkey); ////fprintf(stderr,"HeirClaim() before setting unspendable CC addr2= (%s) addr3= (%s)\n", cp->unspendableaddr2, cp->unspendableaddr3); @@ -945,9 +989,9 @@ template UniValue HeirClaim(uint256 fundingtxid, uint64_t txfee //CCaddr3set(cp, Helper::getMyEval(), heirPubkey, myprivkey, coinaddr); ////fprintf(stderr, "HeirClaim() after setting unspendable CC addr2=(%s) addr3=(%s)\n", cp->unspendableaddr2, cp->unspendableaddr3); - CCaddrTokens1of2set(cp, ownerPubkey, heirPubkey, coinaddr); + Helper::CCaddrCoinsOrTokens1of2set(cp, ownerPubkey, heirPubkey, coinaddr); - // add 1of2 vout validation pubkeys: + // add 1of2 vout validation pubkeys (this is for tokens): std::vector voutTokenPubkeys; voutTokenPubkeys.push_back(ownerPubkey); voutTokenPubkeys.push_back(heirPubkey); @@ -1017,15 +1061,22 @@ UniValue HeirInfo(uint256 fundingtxid) // get initial funding tx and set it as initial lasttx: if (myGetTransaction(fundingtxid, fundingtx, hashBlock) && fundingtx.vout.size()) { - int32_t heirType = NOT_HEIR; const bool noLogging = true; + uint8_t evalCodeTokens = 0; + std::vector voutPubkeys; + std::vector vopretExtra; - if (DecodeHeirOpRet(fundingtx.vout[fundingtx.vout.size() - 1].scriptPubKey, dummyTokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, noLogging) == 'F') - heirType = HEIR_COINS; - else if (DecodeHeirOpRet(fundingtx.vout[fundingtx.vout.size() - 1].scriptPubKey, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, noLogging) == 'F') + CScript heirScript = fundingtx.vout[fundingtx.vout.size() - 1].scriptPubKey; + int32_t heirType = HEIR_COINS; + + uint8_t funcId = DecodeTokenOpRet(heirScript, evalCodeTokens, tokenid, voutPubkeys, vopretExtra); + if (funcId != 0) { + heirScript = CScript(vopretExtra); heirType = HEIR_TOKENS; - else - { + } + funcId = DecodeHeirOpRet(heirScript, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, noLogging); + + if( funcId == 0 ) { std::cerr << "HeirInfo() initial tx F not found for this fundingtx" << std::endl; result.push_back(Pair("result", "error")); result.push_back(Pair("error", "initial tx F not found")); @@ -1033,10 +1084,7 @@ UniValue HeirInfo(uint256 fundingtxid) } struct CCcontract_info *cp, C; - if (heirType == HEIR_COINS) - cp = CCinit(&C, CoinHelper::getMyEval()); - else - cp = CCinit(&C, TokenHelper::getMyEval()); + cp = CCinit(&C, EVAL_HEIR); uint8_t hasHeirSpendingBegun = 0; @@ -1174,14 +1222,24 @@ template void _HeirList(struct CCcontract_info *cp, UniValue &r //std::cerr << "HeirList() checking txid=" << txid.GetHex() << " vout=" << vout << '\n'; - CTransaction inittx; - if (GetTransaction(txid, inittx, hashBlock, false) != 0 && (inittx.vout.size() - 1) > 0) { + CTransaction fundingtx; + if (GetTransaction(txid, fundingtx, hashBlock, false) != 0 && (fundingtx.vout.size() - 1) > 0) { CPubKey ownerPubkey, heirPubkey; std::string heirName; int64_t inactivityTimeSec; const bool noLogging = true; - uint8_t funcId = DecodeHeirOpRet(inittx.vout[inittx.vout.size() - 1].scriptPubKey, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, noLogging); + uint8_t evalCodeTokens = 0; + uint256 tokenid; + std::vector vopretExtra; + std::vector voutPubkeys; + + CScript heirScript = fundingtx.vout[fundingtx.vout.size() - 1].scriptPubKey; + uint8_t funcId = DecodeTokenOpRet(heirScript, evalCodeTokens, tokenid, voutPubkeys, vopretExtra); + if (funcId != 0) { + heirScript = CScript(vopretExtra); + } + funcId = DecodeHeirOpRet(heirScript, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, noLogging); // note: if it is not Heir token funcId would be equal to 0 if (funcId == 'F') { diff --git a/src/cc/heir_validate.h b/src/cc/heir_validate.h index 5b5df2110..0094f5f4c 100644 --- a/src/cc/heir_validate.h +++ b/src/cc/heir_validate.h @@ -14,19 +14,21 @@ CScript EncodeHeirCreateOpRet(uint8_t funcid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName); CScript EncodeHeirOpRet(uint8_t funcid, uint256 fundingtxid, uint8_t isHeirSpendingBegan); // makes token opret -CScript EncodeHeirTokensCreateOpRet(uint8_t funcid, uint256 tokenid, std::vector voutPubkeys, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string hearName); -CScript EncodeHeirTokensOpRet(uint8_t funcid, uint256 tokenid, std::vector voutPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan); +//CScript EncodeHeirTokensCreateOpRet(uint8_t funcid, uint256 tokenid, std::vector voutPubkeys, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string hearName); +//CScript EncodeHeirTokensOpRet(uint8_t funcid, uint256 tokenid, std::vector voutPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan); template uint256 FindLatestFundingTx(uint256 fundingtxid, uint256 &tokenid, CScript& opRetScript, uint8_t &isHeirSpendingBegan); -template uint8_t DecodeHeirOpRet(CScript scriptPubKey, uint256 &tokenid, uint256& fundingtxid, uint8_t &isHeirSpendingBegan, bool noLogging = false); -template uint8_t DecodeHeirOpRet(CScript scriptPubKey, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, bool noLogging = false); +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); + +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 bool isMyFuncId(uint8_t funcid) { return IS_CHARINSTR(funcid, "FAC"); } - static uint8_t getMyEval() { return EVAL_HEIR; } static int64_t addOwnerInputs(struct CCcontract_info* cp, uint256 dummyid, CMutableTransaction& mtx, CPubKey ownerPubkey, int64_t total, int32_t maxinputs) { return AddNormalinputs(mtx, ownerPubkey, total, maxinputs); } @@ -40,43 +42,58 @@ public: static CScript makeClaimOpRet(uint256 dummyid, std::vector dummyPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { return EncodeHeirOpRet((uint8_t)'C', fundingtxid, isHeirSpendingBegan); } - - static bool isSpendingTx(uint8_t funcid) { return (funcid == 'C'); } - + 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(struct CCcontract_info* cp, char *coinaddr, CPubKey ownerPubkey, CPubKey heirPubkey) { + return GetCCaddress1of2(cp, coinaddr, ownerPubkey, heirPubkey); + } + static void CCaddrCoinsOrTokens1of2set(struct CCcontract_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 bool isMyFuncId(uint8_t funcid) { return IS_CHARINSTR(funcid, "FAC"); } - static uint8_t getMyEval() { return EVAL_TOKENS; } static int64_t addOwnerInputs(struct CCcontract_info* cp, uint256 tokenid, CMutableTransaction& mtx, CPubKey ownerPubkey, int64_t total, int32_t maxinputs) { return AddTokenCCInputs(cp, mtx, ownerPubkey, tokenid, total, maxinputs); } static CScript makeCreateOpRet(uint256 tokenid, std::vector voutTokenPubkeys, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName) { - return EncodeHeirTokensCreateOpRet((uint8_t)'F', tokenid, voutTokenPubkeys, ownerPubkey, heirPubkey, inactivityTimeSec, heirName); + return EncodeTokenOpRet((uint8_t)'t', EVAL_TOKENS, tokenid, voutTokenPubkeys, + EncodeHeirCreateOpRet((uint8_t)'F', ownerPubkey, heirPubkey, inactivityTimeSec, heirName)); } static CScript makeAddOpRet(uint256 tokenid, std::vector voutTokenPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { - return EncodeHeirTokensOpRet((uint8_t)'A', tokenid, voutTokenPubkeys, fundingtxid, isHeirSpendingBegan); + return EncodeTokenOpRet((uint8_t)'t', EVAL_TOKENS, tokenid, voutTokenPubkeys, + EncodeHeirOpRet((uint8_t)'A', fundingtxid, isHeirSpendingBegan)); } static CScript makeClaimOpRet(uint256 tokenid, std::vector voutTokenPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { - return EncodeHeirTokensOpRet((uint8_t)'C', tokenid, voutTokenPubkeys, fundingtxid, isHeirSpendingBegan); + return EncodeTokenOpRet((uint8_t)'t', EVAL_TOKENS, tokenid, voutTokenPubkeys, + EncodeHeirOpRet((uint8_t)'C', fundingtxid, isHeirSpendingBegan)); } - static bool isSpendingTx(uint8_t funcid) { return (funcid == 'C'); } - + 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); + return MakeTokensCC1vout(EVAL_HEIR, amount, myPubkey); } static CTxOut makeClaimerVout(int64_t amount, CPubKey myPubkey) { - return MakeCC1vout(EVAL_TOKENS, amount, myPubkey); + return MakeTokensCC1vout(EVAL_HEIR, amount, myPubkey); + } + static bool GetCoinsOrTokensCCaddress1of2(struct CCcontract_info* cp, char *coinaddr, CPubKey ownerPubkey, CPubKey heirPubkey) { + return GetTokensCCaddress1of2(cp, coinaddr, ownerPubkey, heirPubkey); + } + + static void CCaddrCoinsOrTokens1of2set(struct CCcontract_info* cp, CPubKey ownerPubkey, CPubKey heirPubkey, char *coinaddr) { + CCaddrTokens1of2set(cp, ownerPubkey, heirPubkey, coinaddr); } }; @@ -307,11 +324,8 @@ public: 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; } @@ -350,14 +364,12 @@ private: 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) {} @@ -390,13 +402,28 @@ public: virtual bool validateVout(CTxOut vout, std::string& message) const { //std::cerr << "CCC1of2AddressValidator::validateVout() entered" << std::endl; - uint8_t funcId; CPubKey ownerPubkey, heirPubkey; int64_t inactivityTime; std::string heirName; uint256 tokenid; + + uint8_t evalCodeTokens = 0; + std::vector vopretExtra; + std::vector dummyVoutPubkeys; - if ((funcId = DecodeHeirOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName)) == 0) { + CScript heirScript = m_fundingOpretScript; + if (typeid(Helper) == typeid(TokenHelper)) { + if (DecodeTokenOpRet(heirScript, evalCodeTokens, tokenid, dummyVoutPubkeys, vopretExtra) != 0) { + heirScript = CScript(vopretExtra); + } + else { + message = m_customMessage + std::string(" invalid token opreturn format"); + std::cerr << "CCC1of2AddressValidator::validateVout() exits with false: " << message << std::endl; + return false; + } + } + uint8_t funcId = DecodeHeirOpRet(heirScript, ownerPubkey, heirPubkey, inactivityTime, heirName, false); + if (funcId == 0) { message = m_customMessage + std::string(" invalid opreturn format"); std::cerr << "CCC1of2AddressValidator::validateVout() exits with false: " << message << std::endl; return false; @@ -443,21 +470,35 @@ public: { //std::cerr << "CMyPubkeyVoutValidator::validateVout() entered" << std::endl; - uint8_t funcId; CPubKey ownerPubkey, heirPubkey; int64_t inactivityTime; std::string heirName; uint256 tokenid; ///std::cerr << "CMyPubkeyVoutValidator::validateVout() m_opRetScript=" << m_opRetScript.ToString() << std::endl; + + uint8_t evalCodeTokens = 0; + std::vector vopretExtra; + std::vector dummyVoutPubkeys; + + CScript ownerScript; + CScript heirScript = m_fundingOpretScript; + if (typeid(Helper) == typeid(TokenHelper)) { + if (DecodeTokenOpRet(heirScript, evalCodeTokens, tokenid, dummyVoutPubkeys, vopretExtra) != 0) { + heirScript = CScript(vopretExtra); + } + else { + message = std::string("invalid token opreturn format"); + return false; + } + } // get both pubkeys: - if ((funcId = DecodeHeirOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName)) == 0) { + uint8_t funcId = DecodeHeirOpRet(heirScript, ownerPubkey, heirPubkey, inactivityTime, heirName, false); + if (funcId == 0) { message = std::string("invalid opreturn format"); return false; } - CScript ownerScript; - CScript heirScript; if (m_checkNormals) { ownerScript = CoinHelper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey; heirScript = CoinHelper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey; @@ -474,7 +515,6 @@ public: // 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; @@ -503,15 +543,28 @@ public: { //std::cerr << "CHeirSpendValidator::validateVout() entered" << std::endl; - uint8_t funcId; CPubKey ownerPubkey, heirPubkey; int64_t inactivityTime; std::string heirName; uint256 tokenid; + uint8_t evalCodeTokens = 0; + std::vector vopretExtra; + std::vector dummyVoutPubkeys; + + CScript heirScript = m_fundingOpretScript; + if (typeid(Helper) == typeid(TokenHelper)) { + if (DecodeTokenOpRet(heirScript, evalCodeTokens, tokenid, dummyVoutPubkeys, vopretExtra) != 0) { + heirScript = CScript(vopretExtra); + } else { + message = std::string("invalid token opreturn format"); + return false; + } + } // get heir pubkey: - if ((funcId = DecodeHeirOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, false)) == 0) { + uint8_t funcId = DecodeHeirOpRet(heirScript, ownerPubkey, heirPubkey, inactivityTime, heirName, false); + if (funcId == 0) { message = std::string("invalid opreturn format"); return false; } @@ -560,28 +613,54 @@ public: { //std::cerr << "COpRetValidator::validateVout() entered" << std::endl; - uint8_t funcId, initialFuncId; // do not check heir name - uint256 fundingTxidInOpret = zeroid, dummyTxid, tokenid, initialTokenid; + uint256 fundingTxidInOpret = zeroid, dummyTxid, tokenid = zeroid, initialTokenid = zeroid; uint8_t dummyIsHeirSpendingBegan; - if ((funcId = DecodeHeirOpRet(vout.scriptPubKey, tokenid, fundingTxidInOpret, dummyIsHeirSpendingBegan)) == 0) { + uint8_t evalCodeTokens = 0; + std::vector vopretExtra; + std::vector dummyVoutPubkeys; + + CScript heirScript = vout.scriptPubKey; + if (typeid(Helper) == typeid(TokenHelper)) { + if (DecodeTokenOpRet(heirScript, evalCodeTokens, tokenid, dummyVoutPubkeys, vopretExtra) != 0) { + heirScript = CScript(vopretExtra); + } else { + message = std::string("invalid token opreturn format"); + return false; + } + } + uint8_t funcId = DecodeHeirOpRet(heirScript, fundingTxidInOpret, dummyIsHeirSpendingBegan); + if (funcId == 0) { message = std::string("invalid opreturn format"); return false; } - if ((initialFuncId = DecodeHeirOpRet(m_fundingOpretScript, initialTokenid, dummyTxid, dummyIsHeirSpendingBegan)) == 0) { + + heirScript = m_fundingOpretScript; + if (typeid(Helper) == typeid(TokenHelper)) { + if (DecodeTokenOpRet(heirScript, evalCodeTokens, initialTokenid, dummyVoutPubkeys, vopretExtra) != 0) { + heirScript = CScript(vopretExtra); + } else { + message = std::string("invalid initial token opreturn format"); + return false; + } + } + uint8_t initialFuncId = DecodeHeirOpRet(heirScript, dummyTxid, dummyIsHeirSpendingBegan); + if (initialFuncId == 0) { message = std::string("invalid initial tx opreturn format"); return false; } // validation rules: - if (!Helper::isMyFuncId(funcId)) { + if (!isMyFuncId(funcId)) { message = std::string("invalid funcid in opret"); return false; } - if(tokenid != initialTokenid ) { - message = std::string("invalid tokenid 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;