From e583658d144ae7b1fdcb9a64477f5760b169c860 Mon Sep 17 00:00:00 2001 From: dimxy Date: Mon, 11 Feb 2019 19:04:52 +0500 Subject: [PATCH 1/2] removed non-fungible data from 't' opret added fieldId for non-fungible data in 'c' opret changed to cc marker int tokencreate, added in rogue_finishgame (tokenlist supports old and new markers) --- src/cc/CCassets.h | 2 +- src/cc/CCassetsCore.cpp | 44 ++------- src/cc/CCassetstx.cpp | 24 ++--- src/cc/CCinclude.h | 17 ++-- src/cc/CCtokens.cpp | 214 +++++++++++++++++++++++----------------- src/cc/CCtokens.h | 7 -- src/cc/assets.cpp | 2 +- src/cc/rogue_rpc.cpp | 35 +++++-- 8 files changed, 180 insertions(+), 165 deletions(-) diff --git a/src/cc/CCassets.h b/src/cc/CCassets.h index c4623ccbb..82fbcb961 100644 --- a/src/cc/CCassets.h +++ b/src/cc/CCassets.h @@ -30,7 +30,7 @@ bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx // CCassetsCore CScript EncodeAssetOpRet(uint8_t assetFuncId, uint256 assetid2, int64_t price, std::vector origpubkey); -uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &assetsEvalCode, uint256 &tokenid, uint256 &assetid2, int64_t &price, std::vector &origpubkey, std::vector &vopretNonfungible); +uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &assetsEvalCode, uint256 &tokenid, uint256 &assetid2, int64_t &price, std::vector &origpubkey); bool SetAssetOrigpubkey(std::vector &origpubkey,int64_t &price,const CTransaction &tx); int64_t IsAssetvout(struct CCcontract_info *cp, int64_t &price, std::vector &origpubkey, const CTransaction& tx, int32_t v, uint256 refassetid); bool ValidateBidRemainder(int64_t remaining_price,int64_t remaining_nValue,int64_t orig_nValue,int64_t received_nValue,int64_t paidprice,int64_t totalprice); diff --git a/src/cc/CCassetsCore.cpp b/src/cc/CCassetsCore.cpp index fa8b1df71..62d57e71c 100644 --- a/src/cc/CCassetsCore.cpp +++ b/src/cc/CCassetsCore.cpp @@ -279,9 +279,8 @@ bool DecodeAssetCreateOpRet(const CScript &scriptPubKey, std::vector &o return(0); } */ -uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &assetsEvalCode, uint256 &tokenid, uint256 &assetid2, int64_t &price, std::vector &origpubkey, std::vector &vopretNonfungible) +uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &assetsEvalCode, uint256 &tokenid, uint256 &assetid2, int64_t &price, std::vector &origpubkey) { - std::vector vopret1, vopret2; std::vector vopretAssets; //, vopretAssetsStripped; uint8_t *script, funcId = 0, assetsFuncId = 0, dummyEvalCode, dummyAssetFuncId; uint256 dummyTokenid; @@ -294,30 +293,9 @@ uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &assetsEvalCo assetsFuncId = 0; // First - decode token opret: - funcId = DecodeTokenOpRet(scriptPubKey, dummyEvalCode, tokenid, voutPubkeysDummy, vopret1, vopret2); + funcId = DecodeTokenOpRet(scriptPubKey, dummyEvalCode, tokenid, voutPubkeysDummy, vopretAssets); LOGSTREAM((char *)"ccassets", CCLOG_DEBUG2, stream << "DecodeAssetTokenOpRet() from DecodeTokenOpRet returned funcId=" << (int)funcId << std::endl); - if (!vopret2.empty()) { - // if there are both oprets then order is like this: - vopretNonfungible = vopret1; - vopretAssets = vopret2; - } - else { - // if only one opret - it is assets opret: - vopretNonfungible.clear(); - vopretAssets = vopret1; - } - - - /*GetOpReturnData(scriptPubKey, vopret); - script = (uint8_t *)vopret.data(); - if (script == 0) { - std::cerr << "DecodeAssetOpRet() script is empty" << std::endl; - return (uint8_t)0; - }*/ - //bool isEof = true; // NOTE: if parse error occures, parse might not be able to set error. It is safer to treat that it was eof if it is not set! - //bool result = E_UNMARSHAL(vopret, ss >> evalCodeInOpret; ss >> funcId; ss >> tokenid; ss >> assetFuncId; isEof = ss.eof()); - if (funcId == 0 || vopretAssets.size() < 2) { LOGSTREAM((char *)"ccassets", CCLOG_INFO, stream << "DecodeAssetTokenOpRet() incorrect opret or no asset's payload" << " funcId=" << (int)funcId << " vopretAssets.size()=" << vopretAssets.size() << std::endl); return (uint8_t)0; @@ -363,13 +341,12 @@ uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &assetsEvalCo } break; default: - LOGSTREAM((char *)"ccassets", CCLOG_INFO, stream << "DecodeAssetTokenOpRet() illegal assetFuncId=" << (int)funcId << std::endl); break; } } } - LOGSTREAM((char *)"ccassets", CCLOG_INFO, stream << "DecodeAssetTokenOpRet() no asset's payload or incorrect assets evalcode" << " funcId=" << (int)funcId << " vopretAssets.size()=" << vopretAssets.size() << " assetsEvalCode=" << assetsEvalCode << " assetsFuncId=" << assetsFuncId << std::endl); + LOGSTREAM((char *)"ccassets", CCLOG_INFO, stream << "DecodeAssetTokenOpRet() no asset's payload or incorrect assets funcId or evalcode" << " funcId=" << (int)funcId << " vopretAssets.size()=" << vopretAssets.size() << " assetsEvalCode=" << assetsEvalCode << " assetsFuncId=" << assetsFuncId << std::endl); return (uint8_t)0; } @@ -378,9 +355,8 @@ bool SetAssetOrigpubkey(std::vector &origpubkey,int64_t &price,const CT { uint256 assetid,assetid2; uint8_t evalCode; - std::vector vopretNonfungibleDummy; - if ( tx.vout.size() > 0 && DecodeAssetTokenOpRet(tx.vout[tx.vout.size()-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey, vopretNonfungibleDummy) != 0 ) + if ( tx.vout.size() > 0 && DecodeAssetTokenOpRet(tx.vout[tx.vout.size()-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey) != 0 ) return(true); else return(false); @@ -396,10 +372,9 @@ bool GetAssetorigaddrs(struct CCcontract_info *cp, char *origCCaddr, char *origN std::vector origpubkey; CScript script; uint8_t evalCode; - std::vector vopretNonfungibleDummy; n = vintx.vout.size(); - if( n == 0 || (vintxFuncId = DecodeAssetTokenOpRet(vintx.vout[n-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey, vopretNonfungibleDummy)) == 0 ) + if( n == 0 || (vintxFuncId = DecodeAssetTokenOpRet(vintx.vout[n-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey)) == 0 ) return(false); bool bGetCCaddr = false; @@ -444,9 +419,8 @@ int64_t AssetValidateCCvin(struct CCcontract_info *cp,Eval* eval,char *origCCadd int64_t tmpprice; std::vector tmporigpubkey; uint8_t evalCode; - std::vector vopretNonfungibleDummy; - funcid = DecodeAssetTokenOpRet(tx.vout[tx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, tmpprice, tmporigpubkey, vopretNonfungibleDummy); + funcid = DecodeAssetTokenOpRet(tx.vout[tx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, tmpprice, tmporigpubkey); } if( tx.vin.size() < 2 ) @@ -495,7 +469,6 @@ int64_t AssetValidateCCvin(struct CCcontract_info *cp,Eval* eval,char *origCCadd int64_t AssetValidateBuyvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmpprice,std::vector &tmporigpubkey,char *CCaddr,char *origaddr,const CTransaction &tx,uint256 refassetid) { CTransaction vinTx; int64_t nValue; uint256 assetid,assetid2; uint8_t funcid, evalCode; - std::vector vopretNonfungibleDummy; CCaddr[0] = origaddr[0] = 0; @@ -504,7 +477,7 @@ int64_t AssetValidateBuyvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmppr return(0); else if ( vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 ) return eval->Invalid("invalid normal vout0 for buyvin"); - else if ((funcid = DecodeAssetTokenOpRet(vinTx.vout[vinTx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, tmpprice, tmporigpubkey, vopretNonfungibleDummy)) == 'b' && + else if ((funcid = DecodeAssetTokenOpRet(vinTx.vout[vinTx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, tmpprice, tmporigpubkey)) == 'b' && vinTx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 ) // marker is only in 'b'? return eval->Invalid("invalid normal vout1 for buyvin"); else @@ -539,12 +512,11 @@ bool ValidateAssetOpret(CTransaction tx, int32_t v, uint256 assetid, int64_t &pr uint256 assetidOpret, assetidOpret2; uint8_t funcid, evalCode; - std::vector vopretNonfungibleDummy; // this is just for log messages indentation fur debugging recursive calls: int32_t n = tx.vout.size(); - if ((funcid = DecodeAssetTokenOpRet(tx.vout[n - 1].scriptPubKey, evalCode, assetidOpret, assetidOpret2, price, origpubkey, vopretNonfungibleDummy)) == 0) + if ((funcid = DecodeAssetTokenOpRet(tx.vout[n - 1].scriptPubKey, evalCode, assetidOpret, assetidOpret2, price, origpubkey)) == 0) { std::cerr << "ValidateAssetOpret() DecodeAssetTokenOpRet returned funcId=0 for opret from txid=" << tx.GetHash().GetHex() << std::endl; return(false); diff --git a/src/cc/CCassetstx.cpp b/src/cc/CCassetstx.cpp index 79a8fb77a..7673724f1 100644 --- a/src/cc/CCassetstx.cpp +++ b/src/cc/CCassetstx.cpp @@ -76,18 +76,17 @@ UniValue AssetOrders(uint256 refassetid) uint256 txid, hashBlock, assetid, assetid2; int64_t price; std::vector origpubkey; - std::vector vopretNonfungible; CTransaction vintx; uint8_t funcid, evalCode; char numstr[32], funcidstr[16], origaddr[64], assetidstr[65]; txid = it->first.txhash; //std::cerr << "addOrders() txid=" << txid.GetHex() << std::endl; - if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) + if ( GetTransaction(txid, vintx, hashBlock, false) != 0 ) { // for logging: funcid = DecodeAssetOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey); //std::cerr << "addOrders() vintx.vout.size()=" << vintx.vout.size() << " funcid=" << (char)(funcid ? funcid : ' ') << " assetid=" << assetid.GetHex() << std::endl; - if (vintx.vout.size() > 0 && (funcid = DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey, vopretNonfungible)) != 0) + if (vintx.vout.size() > 0 && (funcid = DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey)) != 0) { if (refassetid != zero && assetid != refassetid) { @@ -402,7 +401,7 @@ std::string CreateSell(int64_t txfee,int64_t askamount,uint256 assetid,int64_t p voutTokenPubkeys.push_back(unspendableAssetsPubkey); return FinalizeCCTx(mask, cpTokens, mtx, mypk, txfee, - EncodeTokenOpRet(assetid, voutTokenPubkeys, vopretNonfungible, + EncodeTokenOpRet(assetid, voutTokenPubkeys, EncodeAssetOpRet('s', zeroid, pricetotal, Mypubkey()))); } else { @@ -506,11 +505,12 @@ std::string CancelBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid) if (GetTransaction(bidtxid, vintx, hashBlock, false) != 0) { std::vector vopretNonfungible; + GetNonfungibleData(assetid, vopretNonfungible); bidamount = vintx.vout[0].nValue; mtx.vin.push_back(CTxIn(bidtxid, 0, CScript())); // coins in Assets - if((funcid=DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, dummyEvalCode, dummyAssetid, dummyAssetid2, dummyPrice, dummyOrigpubkey, vopretNonfungible))!=0) + if((funcid=DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, dummyEvalCode, dummyAssetid, dummyAssetid2, dummyPrice, dummyOrigpubkey))!=0) { if (funcid == 's') mtx.vin.push_back(CTxIn(bidtxid, 1, CScript())); // spend marker if funcid='b' else if (funcid=='S') mtx.vin.push_back(CTxIn(bidtxid, 3, CScript())); // spend marker if funcid='B' @@ -522,8 +522,8 @@ std::string CancelBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid) std::vector voutTokenPubkeys; // should be empty, no token vouts return(FinalizeCCTx(mask, cpAssets, mtx, mypk, txfee, - EncodeTokenOpRet(assetid, voutTokenPubkeys, vopretNonfungible, - EncodeAssetOpRet('o', zeroid, 0, Mypubkey())))); + EncodeTokenOpRet(assetid, voutTokenPubkeys, + EncodeAssetOpRet('o', zeroid, 0, Mypubkey())))); } } return(""); @@ -555,10 +555,12 @@ std::string CancelSell(int64_t txfee,uint256 assetid,uint256 asktxid) if (GetTransaction(asktxid, vintx, hashBlock, false) != 0) { std::vector vopretNonfungible; + GetNonfungibleData(assetid, vopretNonfungible); + askamount = vintx.vout[0].nValue; mtx.vin.push_back(CTxIn(asktxid, 0, CScript())); - if ((funcid=DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, dummyEvalCode, dummyAssetid, dummyAssetid2, dummyPrice, dummyOrigpubkey, vopretNonfungible))!=0) + if ((funcid=DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, dummyEvalCode, dummyAssetid, dummyAssetid2, dummyPrice, dummyOrigpubkey))!=0) { if (funcid == 's') mtx.vin.push_back(CTxIn(asktxid, 1, CScript())); // marker if funcid='s' @@ -588,7 +590,7 @@ std::string CancelSell(int64_t txfee,uint256 assetid,uint256 asktxid) CCaddr2set(cpAssets, EVAL_TOKENS, unspendableAssetsPk, unspendableAssetsPrivkey, unspendableAssetsAddr); return(FinalizeCCTx(mask, cpAssets, mtx, mypk, txfee, - EncodeTokenOpRet(assetid, voutTokenPubkeys, vopretNonfungible, + EncodeTokenOpRet(assetid, voutTokenPubkeys, EncodeAssetOpRet('x', zeroid, 0, Mypubkey())))); } } @@ -675,7 +677,7 @@ std::string FillBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid,int64_t f voutTokenPubkeys.push_back(pubkey2pk(origpubkey)); return(FinalizeCCTx(mask, cpTokens, mtx, mypk, txfee, - EncodeTokenOpRet(assetid, voutTokenPubkeys, vopretNonfungible, + EncodeTokenOpRet(assetid, voutTokenPubkeys, EncodeAssetOpRet('B', zeroid, remaining_required, origpubkey)))); } else return("dont have any assets to fill bid"); } @@ -800,7 +802,7 @@ std::string FillSell(int64_t txfee, uint256 assetid, uint256 assetid2, uint256 a cpAssets->additionalTokensEvalcode2 = additionalTokensEvalcode2; return(FinalizeCCTx(mask, cpAssets, mtx, mypk, txfee, - EncodeTokenOpRet(assetid, voutTokenPubkeys, vopretNonfungible, + EncodeTokenOpRet(assetid, voutTokenPubkeys, EncodeAssetOpRet(assetid2 != zeroid ? 'E' : 'S', assetid2, remaining_nValue, origpubkey)))); } else { CCerror = strprintf("filltx not enough utxos"); diff --git a/src/cc/CCinclude.h b/src/cc/CCinclude.h index 57f42f064..6018114a0 100644 --- a/src/cc/CCinclude.h +++ b/src/cc/CCinclude.h @@ -67,6 +67,12 @@ one other technical note is that komodod has the insight-explorer extensions bui #include "../komodo_cJSON.h" +// opret data block ids: + enum { + OPRETID_NONFUNGIBLEDATA = 0x11 + // TODO: OPRETID_ASSETSDATA = 0x12 +}; + struct CC_utxo { uint256 txid; @@ -173,11 +179,10 @@ bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx); CScript EncodeTokenCreateOpRet(uint8_t funcid, std::vector origpubkey, std::string name, std::string description, std::vector vopretNonfungible); CScript EncodeTokenOpRet(uint8_t tokenFuncId, uint8_t evalCodeInOpret, uint256 tokenid, std::vector voutPubkeys, CScript payload); //old version CScript EncodeTokenOpRet(uint256 tokenid, std::vector voutPubkeys, CScript payload); -CScript EncodeTokenOpRet(uint256 tokenid, std::vector voutPubkeys, std::vector vpayloadNonfungible, CScript payload); uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey, std::vector &origpubkey, std::string &name, std::string &description); -uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey, std::vector &origpubkey, std::string &name, std::string &description, std::vector &vopretNonfungible); -uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCode, uint256 &tokenid, std::vector &voutPubkeys, std::vector &vopretExtra); -uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCode, uint256 &tokenid, std::vector &voutPubkeys, std::vector &vopret1, std::vector &vopret2); +uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey, std::vector &origpubkey, std::string &name, std::string &description, std::vector &vopretNonfungible); +uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCodeTokens, uint256 &tokenid, std::vector &voutPubkeys, std::vector &vopretExtra); +void GetNonfungibleData(uint256 tokenid, std::vector &vopretNonfungible); uint8_t DecodeOraclesData(const CScript &scriptPubKey,uint256 &oracletxid,uint256 &batontxid,CPubKey &pk,std::vector &data); int32_t oracle_format(uint256 *hashp,int64_t *valp,char *str,uint8_t fmt,uint8_t *data,int32_t offset,int32_t datalen); @@ -186,7 +191,7 @@ int32_t oracle_format(uint256 *hashp,int64_t *valp,char *str,uint8_t fmt,uint8_t // CCcustom CPubKey GetUnspendable(struct CCcontract_info *cp,uint8_t *unspendablepriv); -uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCodeTokens, uint256 &tokenid, std::vector &voutPubkeys, std::vector &vopret1, std::vector &vopret2); +//uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCodeTokens, uint256 &tokenid, std::vector &voutPubkeys, std::vector &vopret1, std::vector &vopret2); // CCutils bool priv2addr(char *coinaddr,uint8_t buf33[33],uint8_t priv32[32]); @@ -262,7 +267,7 @@ UniValue ValueFromAmount(const CAmount& amount); #define CCLOG_DEBUG2 2 #define CCLOG_DEBUG3 3 template -inline void CCLogPrintStream(char *category, int level, T print_to_stream) +inline void CCLogPrintStream(const char *category, int level, T print_to_stream) { std::ostringstream stream; print_to_stream(stream); diff --git a/src/cc/CCtokens.cpp b/src/cc/CCtokens.cpp index fedc78151..f5eda7d31 100644 --- a/src/cc/CCtokens.cpp +++ b/src/cc/CCtokens.cpp @@ -50,18 +50,15 @@ CScript EncodeTokenCreateOpRet(uint8_t funcid, std::vector origpubkey, funcid = 'c'; // override the param opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << origpubkey << name << description; \ - if(!vopretNonfungible.empty()) ss << vopretNonfungible ); + if (!vopretNonfungible.empty()) { \ + ss << (uint8_t)OPRETID_NONFUNGIBLEDATA; \ + ss << vopretNonfungible; \ + }); return(opret); } -// this is for other contracts which use tokens and build customized extra payloads to token's opret: -CScript EncodeTokenOpRet(uint256 tokenid, std::vector voutPubkeys, CScript payload) -{ - std::vector vpayloadNonfungibleEmpty; - return EncodeTokenOpRet(tokenid, voutPubkeys, vpayloadNonfungibleEmpty, payload); -} -CScript EncodeTokenOpRet(uint256 tokenid, std::vector voutPubkeys, std::vector vpayloadNonfungible, CScript payload) +CScript EncodeTokenOpRet(uint256 tokenid, std::vector voutPubkeys, CScript payload) { CScript opret; uint8_t tokenFuncId = 't'; @@ -73,7 +70,7 @@ CScript EncodeTokenOpRet(uint256 tokenid, std::vector voutPubkeys, std: if (voutPubkeys.size() >= 0 && voutPubkeys.size() <= 2) ccType = voutPubkeys.size(); else { - LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "EncodeTokenOpRet voutPubkeys.size()=" << voutPubkeys.size() << " not supported" << std::endl); + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "EncodeTokenOpRet voutPubkeys.size()=" << voutPubkeys.size() << " not supported" << std::endl); } std::vector vpayload; @@ -82,10 +79,11 @@ CScript EncodeTokenOpRet(uint256 tokenid, std::vector voutPubkeys, std: opret << OP_RETURN << E_MARSHAL(ss << evalCodeInOpret << tokenFuncId << tokenid << ccType; \ if (ccType >= 1) ss << voutPubkeys[0]; \ if (ccType == 2) ss << voutPubkeys[1]; \ - if (vpayloadNonfungible.size() > 0) ss << vpayloadNonfungible; \ if (vpayload.size() > 0) ss << vpayload); - // "error 64: scriptpubkey": + // bad opret cases (retries to attach payload without re-serialization): + + // "error 64: scriptpubkey": // if (payload.size() > 0) // opret += payload; @@ -93,8 +91,7 @@ CScript EncodeTokenOpRet(uint256 tokenid, std::vector voutPubkeys, std: // CScript opretPayloadNoOpcode(vpayload); // return opret + opretPayloadNoOpcode; - // how to attach payload without re-serialization: - // sig_aborted: + // error sig_aborted: // opret.resize(opret.size() + vpayload.size()); // CScript::iterator it = opret.begin() + opret.size(); // for (int i = 0; i < vpayload.size(); i++, it++) @@ -117,16 +114,20 @@ uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey, std::vector uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey,std::vector &origpubkey,std::string &name,std::string &description, std::vector &vopretNonfungible) { - std::vector vopret; uint8_t dummyEvalcode, funcid, *script; + std::vector vopret; uint8_t dummyEvalcode, funcid, *script, fieldId = 0; GetOpReturnData(scriptPubKey, vopret); script = (uint8_t *)vopret.data(); if ( script != 0 && vopret.size() > 2 && script[0] == EVAL_TOKENS && script[1] == 'c' ) { - if( E_UNMARSHAL(vopret, ss >> dummyEvalcode; ss >> funcid; ss >> origpubkey; ss >> name; ss >> description; \ + if (E_UNMARSHAL(vopret, ss >> dummyEvalcode; ss >> funcid; ss >> origpubkey; ss >> name; ss >> description; \ // we suppose in 'c' opret it might be only non-fungible payload and not any assets/heir/etc payloads - if(!ss.eof()) ss >> vopretNonfungible ) ) + if (!ss.eof()) { \ + ss >> fieldId; \ + if( fieldId == OPRETID_NONFUNGIBLEDATA) \ + ss >> vopretNonfungible; \ + })) return(funcid); } return (uint8_t)0; @@ -134,7 +135,7 @@ uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey,std::vector // overload for compatibility allows only usual fungible tokens: // warning: it makes vopret marshalling to CScript because this is what caller would expect -uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCode, uint256 &tokenid, std::vector &voutPubkeys, std::vector &vopretExtra) { +/*uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCode, uint256 &tokenid, std::vector &voutPubkeys, std::vector &vopretExtra) { std::vector vopret1, vopret2; uint8_t funcId = DecodeTokenOpRet(scriptPubKey, evalCode, tokenid, voutPubkeys, vopret1, vopret2); @@ -150,12 +151,12 @@ uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCode, uint256 GetOpReturnData(opretExtra, vopretExtra); return funcId; -} +} */ -uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCodeTokens, uint256 &tokenid, std::vector &voutPubkeys, std::vector &vopret1, std::vector &vopret2) +uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCodeTokens, uint256 &tokenid, std::vector &voutPubkeys, std::vector &vopretExtra) { - std::vector vopret, extra, dummyPubkey; - uint8_t funcId=0, *script, dummyEvalCode, dummyFuncId, ccType; + std::vector vopret, extra, dummyPubkey, vnonfungibleDummy; + uint8_t funcId=0, *script, dummyEvalCode, dummyFuncId, ccType, fieldId = 0; std::string dummyName; std::string dummyDescription; CPubKey voutPubkey1, voutPubkey2; @@ -178,7 +179,7 @@ uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCodeTokens, ui switch( funcId ) { case 'c': - return DecodeTokenCreateOpRet(scriptPubKey, dummyPubkey, dummyName, dummyDescription, vopret1); + return DecodeTokenCreateOpRet(scriptPubKey, dummyPubkey, dummyName, dummyDescription, vnonfungibleDummy); //break; case 't': //not used yet: case 'l': @@ -187,9 +188,7 @@ uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCodeTokens, ui if (ccType >= 1) ss >> voutPubkey1; \ if (ccType == 2) ss >> voutPubkey2; \ isEof = ss.eof(); \ - if (!isEof) ss >> vopret1; \ - isEof = ss.eof(); \ - if (!isEof) { ss >> vopret2; } \ + if (!isEof) ss >> vopretExtra; \ // if something else remains -> bad format isEof = ss.eof()) || !isEof) { @@ -230,7 +229,7 @@ bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction & CTxDestination address; CTransaction vinTx, createTx; uint256 hashBlock, tokenid, tokenid2; int32_t i, starti, numvins, numvouts, preventCCvins, preventCCvouts; int64_t remaining_price, nValue, tokenoshis, outputs, inputs, tmpprice, totalunits, ignore; - std::vector vopret1, vopret2, tmporigpubkey, ignorepubkey; + std::vector vopretExtra, tmporigpubkey, ignorepubkey; uint8_t funcid, evalCodeInOpret; char destaddr[64], origaddr[64], CCaddr[64]; std::vector voutTokenPubkeys; @@ -242,7 +241,11 @@ bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction & outputs = inputs = 0; preventCCvins = preventCCvouts = -1; - if ((funcid = DecodeTokenOpRet(tx.vout[numvouts - 1].scriptPubKey, evalCodeInOpret, tokenid, voutTokenPubkeys, vopret1, vopret2)) == 0) + // check boundaries: + if (numvouts < 1) + return eval->Invalid("no vouts"); + + if ((funcid = DecodeTokenOpRet(tx.vout[numvouts - 1].scriptPubKey, evalCodeInOpret, tokenid, voutTokenPubkeys, vopretExtra)) == 0) return eval->Invalid("TokenValidate: invalid opreturn payload"); LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "TokensValidate funcId=" << (char)(funcid?funcid:' ') << " evalcode=" << cp->evalcode << std::endl); @@ -250,13 +253,11 @@ bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction & if (eval->GetTxUnconfirmed(tokenid, createTx, hashBlock) == 0) return eval->Invalid("cant find token create txid"); //else if (IsCCInput(tx.vin[0].scriptSig) != 0) - // return eval->Invalid("illegal token vin0"); - else if (numvouts < 1) - return eval->Invalid("no vouts"); + // return eval->Invalid("illegal token vin0"); // this validation was removed because some token tx might not have normal vins else if (funcid != 'c') { - if (tokenid == zeroid) - return eval->Invalid("illegal tokenid"); + if (tokenid == zeroid) + return eval->Invalid("illegal tokenid"); else if (!TokensExactAmounts(true, cp, inputs, outputs, eval, tx, tokenid)) { if (!eval->Valid()) return false; //TokenExactAmounts must call eval->Invalid()! @@ -265,14 +266,7 @@ bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction & } } - // non-fungible tokens validation: - std::vector vopretNonfungible; - GetNonfungibleData(tokenid, vopretNonfungible); - if (vopretNonfungible.size() > 0 && vopretNonfungible != vopret1) // assuming tx vopretNonfungible in vopret1 - return eval->Invalid("incorrect or empty non-fungible data"); - - - switch (funcid) + switch (funcid) { case 'c': // create wont be called to be verified as it has no CC inputs //vin.0: normal input @@ -418,7 +412,7 @@ uint8_t ValidateTokenOpret(CTransaction tx, uint256 tokenid) { // goDeeper is true: the func also validates amounts of the passed transaction: // it should be either sum(cc vins) == sum(cc vouts) or the transaction is the 'tokenbase' ('c') tx // checkPubkeys is true: validates if the vout is token vout1 or token vout1of2. Should always be true! -int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, int32_t v, uint256 reftokenid) +int64_t IsTokensvout(bool goDeeper, bool checkPubkeys /*<--not used, always true*/, struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, int32_t v, uint256 reftokenid) { // this is just for log messages indentation fur debugging recursive calls: @@ -458,36 +452,41 @@ int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *c // token opret most important checks (tokenid == reftokenid, tokenid is non-zero, tx is 'tokenbase'): const uint8_t funcId = ValidateTokenOpret(tx, reftokenid); //std::cerr << indentStr << "IsTokensvout() ValidateTokenOpret returned=" << (char)(funcId?funcId:' ') << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; - if (funcId != 0) { - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "IsTokensvout() ValidateTokenOpret returned not-null funcId=" << (char)(funcId?funcId:' ') << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl); + if (funcId != 0) { + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << indentStr << "IsTokensvout() ValidateTokenOpret returned not-null funcId=" << (char)(funcId ? funcId : ' ') << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl); uint8_t dummyEvalCode; uint256 tokenIdOpret; std::vector voutPubkeys; - std::vector vopret1; - std::vector vopret2; - DecodeTokenOpRet(tx.vout.back().scriptPubKey, dummyEvalCode, tokenIdOpret, voutPubkeys, vopret1, vopret2); + std::vector vopretExtra, vopretNonfungible; - if (checkPubkeys && funcId != 'c') { // for 'c' there is no pubkeys + uint8_t evalCode = EVAL_TOKENS; // if both payloads are empty maybe it is a transfer to non-payload-one-eval-token vout like GatewaysClaim + uint8_t evalCode2 = 0; // will be checked if zero or not + + // test vouts for possible token use-cases: + std::vector> testVouts; + + DecodeTokenOpRet(tx.vout.back().scriptPubKey, dummyEvalCode, tokenIdOpret, voutPubkeys, vopretExtra); + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "IsTokensvout() vopretExtra=" << HexStr(vopretExtra) << std::endl); + + GetNonfungibleData(reftokenid, vopretNonfungible); + + // NOTE: evalcode order in vouts is important: + // non-fungible-eval -> EVAL_TOKENS -> assets-eval + + if (vopretNonfungible.size() > 0) + evalCode = vopretNonfungible.begin()[0]; + if (vopretExtra.size() > 0) + evalCode2 = vopretExtra.begin()[0]; + + if (evalCode == EVAL_TOKENS && evalCode2 != 0) { + evalCode = evalCode2; + evalCode2 = 0; + } + + if( /*checkPubkeys &&*/ funcId != 'c' ) { // for 'c' there is no pubkeys // verify that the vout is token by constructing vouts with the pubkeys in the opret: - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "IsTokensvout() vopret1=" << HexStr(vopret1) << std::endl); - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "IsTokensvout() vopret2=" << HexStr(vopret2) << std::endl); - - uint8_t evalCode = EVAL_TOKENS; // if both payloads are empty maybe it is a transfer to non-payload-one-eval-token vout like GatewaysClaim - uint8_t evalCode2 = 0; // will be checked if zero or not - - // NOTE: evalcode order in vouts is important: - // non-fungible-eval -> EVAL_TOKENS -> assets-eval - if (vopret1.size() > 0) { - evalCode = vopret1.begin()[0]; - } - if (vopret2.size() > 0) { - evalCode2 = vopret2.begin()[0]; - } - - // checking vouts for possible token use-cases: - std::vector> testVouts; // maybe this is dual-eval 1 pubkey or 1of2 pubkey vout? if (voutPubkeys.size() >= 1 && voutPubkeys.size() <= 2) { // check dual/three-eval 1 pubkey vout with the first pubkey @@ -541,19 +540,36 @@ int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *c testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, evalCode, tx.vout[v].nValue, *it), std::string("three-eval cc1 self vin pk backward-eval"))); } - // try all test vouts: - for (auto t : testVouts) { - if (t.first == tx.vout[v]) { - LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << indentStr << "IsTokensvout() valid amount=" << tx.vout[v].nValue << " msg=" << t.second << " evalCode=" << (int)evalCode << " evalCode2=" << (int)evalCode2 << " txid=" << tx.GetHash().GetHex() << " tokenid=" << reftokenid.GetHex() << std::endl); - return tx.vout[v].nValue; - } - } - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "IsTokensvout() no valid vouts evalCode=" << (int)evalCode << " evalCode2=" << (int)evalCode2 << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl); } else { - LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << indentStr << "IsTokensvout() returns without pubkey check value=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl); - return tx.vout[v].nValue; + CPubKey origPubkey; + std::vector vorigPubkey; + std::string dummyName, dummyDescription; + std::vector vopret1; + + if (DecodeTokenCreateOpRet(tx.vout.back().scriptPubKey, vorigPubkey, dummyName, dummyDescription, vopret1) == 0) { + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << indentStr << "IsTokensvout() could not decode create opret" << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl); + return 0; + } + + origPubkey = pubkey2pk(vorigPubkey); + + // for 'c' recognize the tokens only to token originator pubkey (but not to unspendable <-- closed sec violation) + // maybe this is like gatewayclaim to single-eval token? + testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, origPubkey), std::string("single-eval cc1 orig-pk"))); + // maybe this is like FillSell for non-fungible token? + if (evalCode != 0) + testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode, tx.vout[v].nValue, origPubkey), std::string("dual-eval-token cc1 orig-pk"))); } + + // try all test vouts: + for (auto t : testVouts) { + if (t.first == tx.vout[v]) { + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "IsTokensvout() valid amount=" << tx.vout[v].nValue << " msg=" << t.second << " evalCode=" << (int)evalCode << " evalCode2=" << (int)evalCode2 << " txid=" << tx.GetHash().GetHex() << " tokenid=" << reftokenid.GetHex() << std::endl); + return tx.vout[v].nValue; + } + } + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "IsTokensvout() no valid vouts evalCode=" << (int)evalCode << " evalCode2=" << (int)evalCode2 << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl); } //std::cerr << indentStr; fprintf(stderr,"IsTokensvout() CC vout v.%d of n=%d amount=%.8f txid=%s\n",v,n,(double)0/COIN, tx.GetHash().GetHex().c_str()); @@ -637,6 +653,7 @@ bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cp, int64_t &inpu return true; } + // get non-fungible data from 'tokenbase' tx (the data might be empty) void GetNonfungibleData(uint256 tokenid, std::vector &vopretNonfungible) { @@ -644,18 +661,18 @@ void GetNonfungibleData(uint256 tokenid, std::vector &vopretNonfungible uint256 hashBlock; if (!myGetTransaction(tokenid, tokenbasetx, hashBlock)) { - LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "SetNonfungibleEvalCode() cound not load token creation tx=" << tokenid.GetHex() << std::endl); + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "GetNonfungibleData() cound not load token creation tx=" << tokenid.GetHex() << std::endl); return; } vopretNonfungible.clear(); // check if it is non-fungible tx and get its second evalcode from non-fungible payload if (tokenbasetx.vout.size() > 0) { - uint8_t dummyEvalCode; - uint256 tokenIdOpret; - std::vector voutPubkeys; + std::vector origpubkey; + std::string name, description; std::vector vopretExtra; - DecodeTokenOpRet(tokenbasetx.vout.back().scriptPubKey, dummyEvalCode, tokenIdOpret, voutPubkeys, vopretNonfungible, vopretExtra); + if (DecodeTokenCreateOpRet(tokenbasetx.vout.back().scriptPubKey, origpubkey, name, description, vopretExtra) == 'c') + vopretNonfungible = vopretExtra; } } @@ -786,7 +803,9 @@ std::string CreateToken(int64_t txfee, int64_t tokensupply, std::string name, st destEvalCode = nonfungibleData.begin()[0]; mtx.vout.push_back(MakeTokensCC1vout(destEvalCode, tokensupply, mypk)); - mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(cp->CChexstr) << OP_CHECKSIG)); + //mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(cp->CChexstr) << OP_CHECKSIG)); // old marker (non-burnable because spending could not be validated) + // NOTE: we should prevent spending fake-tokens from this marker in IsTokenvout(): + mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, txfee, GetUnspendable(cp, NULL))); // new marker to cc addr, burnable and validated return(FinalizeCCTx(0, cp, mtx, mypk, txfee, EncodeTokenCreateOpRet('c', Mypubkey(), name, description, nonfungibleData))); } @@ -838,7 +857,7 @@ std::string TokenTransfer(int64_t txfee, uint256 tokenid, std::vector d std::vector voutTokenPubkeys; voutTokenPubkeys.push_back(pubkey2pk(destpubkey)); // dest pubkey for validating vout - return(FinalizeCCTx(mask, cp, mtx, mypk, txfee, EncodeTokenOpRet(tokenid, voutTokenPubkeys, vopretNonfungible, CScript()))); + return(FinalizeCCTx(mask, cp, mtx, mypk, txfee, EncodeTokenOpRet(tokenid, voutTokenPubkeys, CScript()))); } else { LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "not enough CC token inputs for amount=" << total << std::endl); @@ -913,22 +932,31 @@ UniValue TokenList() { UniValue result(UniValue::VARR); std::vector > addressIndex; + std::vector > addressIndexCCMarker; + struct CCcontract_info *cp, C; uint256 txid, hashBlock; CTransaction vintx; std::vector origpubkey; - std::string name, description; char str[65]; + std::string name, description; cp = CCinit(&C, EVAL_TOKENS); - SetCCtxids(addressIndex, cp->normaladdr); - for (std::vector >::const_iterator it = addressIndex.begin(); it != addressIndex.end(); it++) - { - txid = it->first.txhash; - if (GetTransaction(txid, vintx, hashBlock, false) != 0) - { - if (vintx.vout.size() > 0 && DecodeTokenCreateOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, origpubkey, name, description) != 0) - { - result.push_back(uint256_str(str, txid)); - } - } + + auto addTokenId = [&](uint256 txid) { + if (GetTransaction(txid, vintx, hashBlock, false) != 0) { + if (vintx.vout.size() > 0 && DecodeTokenCreateOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, origpubkey, name, description) != 0) { + result.push_back(txid.GetHex()); + } + } + }; + + SetCCtxids(addressIndex, cp->normaladdr); // find by old normal addr marker + for (std::vector >::const_iterator it = addressIndex.begin(); it != addressIndex.end(); it++) { + addTokenId(it->first.txhash); } + + SetCCunspents(addressIndexCCMarker, cp->unspendableCCaddr); // find by burnable validated cc addr marker + for (std::vector >::const_iterator it = addressIndexCCMarker.begin(); it != addressIndexCCMarker.end(); it++) { + addTokenId(it->first.txhash); + } + return(result); } diff --git a/src/cc/CCtokens.h b/src/cc/CCtokens.h index 4637ba6c9..231b64ac9 100644 --- a/src/cc/CCtokens.h +++ b/src/cc/CCtokens.h @@ -35,11 +35,4 @@ int64_t GetTokenBalance(CPubKey pk, uint256 tokenid); UniValue TokenInfo(uint256 tokenid); UniValue TokenList(); -void GetNonfungibleData(uint256 tokenid, std::vector &vopretNonfungible); - -//this is in CCinclude.h int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs); - -//this is in CCinclude.h uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey,std::vector &origpubkey,std::string &name,std::string &description); - - #endif diff --git a/src/cc/assets.cpp b/src/cc/assets.cpp index 97b01302c..6f93da01b 100644 --- a/src/cc/assets.cpp +++ b/src/cc/assets.cpp @@ -157,7 +157,7 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti if (numvouts == 0) return eval->Invalid("AssetValidate: no vouts"); - if((funcid = DecodeAssetTokenOpRet(tx.vout[numvouts-1].scriptPubKey, evalCodeInOpret, assetid, assetid2, remaining_price, origpubkey, vopretNonfungibleDummy)) == 0 ) + if((funcid = DecodeAssetTokenOpRet(tx.vout[numvouts-1].scriptPubKey, evalCodeInOpret, assetid, assetid2, remaining_price, origpubkey)) == 0 ) return eval->Invalid("AssetValidate: invalid opreturn payload"); // non-fungible tokens support: diff --git a/src/cc/rogue_rpc.cpp b/src/cc/rogue_rpc.cpp index 9c7504e5f..5a1ef60b5 100644 --- a/src/cc/rogue_rpc.cpp +++ b/src/cc/rogue_rpc.cpp @@ -15,6 +15,7 @@ ******************************************************************************/ #include "cJSON.h" +#include "CCinclude.h" #define ROGUE_REGISTRATION 5 #define ROGUE_REGISTRATIONSIZE (100 * 10000) @@ -173,19 +174,27 @@ CScript rogue_highlanderopret(uint8_t funcid,uint256 gametxid,int32_t regslot,CP return(opret); } -uint8_t rogue_highlanderopretdecode(uint256 &gametxid,int32_t ®slot,CPubKey &pk,std::vector &playerdata,CScript scriptPubKey) +uint8_t rogue_highlanderopretdecode(uint256 &gametxid, int32_t ®slot, CPubKey &pk, std::vector &playerdata, CScript scriptPubKey) { - std::vector vopret,vopret2; uint8_t e,f; uint256 tokenid; std::vector voutPubkeys; - GetOpReturnData(scriptPubKey,vopret); - if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> gametxid; ss >> regslot; ss >> pk; ss >> playerdata) != 0 && e == EVAL_ROGUE && (f == 'H' || f == 'Q') ) + std::string name, description; + std::vector vorigPubkey; + std::vector vopretNonfungible, vopret, vopretDummy; + uint8_t e, f; uint256 tokenid; std::vector voutPubkeys; + + GetOpReturnData(scriptPubKey, vopret); + // try no tokens case: + if (vopret.size() > 2 && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> gametxid; ss >> regslot; ss >> pk; ss >> playerdata) != 0 && e == EVAL_ROGUE && (f == 'H' || f == 'Q')) return(f); - else if ( (f= DecodeTokenOpRet(scriptPubKey,e,tokenid,voutPubkeys,vopret,vopret2)) == 'c' || f == 't' ) - { - if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> gametxid; ss >> regslot; ss >> pk; ss >> playerdata) != 0 && e == EVAL_ROGUE && (f == 'H' || f == 'Q') ) + else if (f = DecodeTokenOpRet(scriptPubKey, e, tokenid, voutPubkeys, vopretDummy) != 0) { // it is tokens + if (f != 'c') + GetNonfungibleData(tokenid, vopretNonfungible); //load nonfungible data from the 'tokenbase' tx + + if (vopretNonfungible.size() > 2 && E_UNMARSHAL(vopretNonfungible, ss >> e; ss >> f; ss >> gametxid; ss >> regslot; ss >> pk; ss >> playerdata) != 0 && e == EVAL_ROGUE && (f == 'H' || f == 'Q')) { return(f); } } + return(0); } @@ -670,6 +679,7 @@ UniValue rogue_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) // vout0 -> keystrokes/completion baton CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); UniValue result(UniValue::VOBJ); char destaddr[64],coinaddr[64]; uint256 gametxid,origplayergame,playertxid,hashBlock; int32_t err,maxplayers,gameheight,n,numvouts; int64_t inputsum,buyin,CCchange=0; CPubKey pk,mypk,roguepk,burnpk; CTransaction tx,playertx; std::vector playerdata; std::string rawtx; bits256 t; + if ( txfee == 0 ) txfee = 10000; mypk = pubkey2pk(Mypubkey()); @@ -707,7 +717,7 @@ UniValue rogue_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) CCaddr1of2set(cp,roguepk,roguepk,cp->CCpriv,destaddr); mtx.vout.push_back(MakeTokensCC1vout(cp->evalcode, 1, burnpk)); - std::vector vopretFinish, vopret2; uint8_t e, funcid; uint256 tokenid; std::vector voutPubkeys, voutPubkeysEmpty; int32_t didtx = 0; + std::vector vopretExtra; uint8_t e, funcid; uint256 tokenid; std::vector voutPubkeys, voutPubkeysEmpty; int32_t didtx = 0; CScript opretRegister = rogue_registeropret(gametxid, playertxid); if ( playertxid != zeroid ) { @@ -715,13 +725,13 @@ UniValue rogue_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) voutPubkeysEmpty.push_back(burnpk); if ( GetTransaction(playertxid,playertx,hashBlock,false) != 0 ) { - if ( (funcid= DecodeTokenOpRet(playertx.vout.back().scriptPubKey, e, tokenid, voutPubkeys, vopretFinish, vopret2)) != 0) + if ( (funcid= DecodeTokenOpRet(playertx.vout.back().scriptPubKey, e, tokenid, voutPubkeys, vopretExtra)) != 0) { // if token in the opret didtx = 1; if ( funcid == 'c' ) tokenid = playertxid; rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, - EncodeTokenOpRet(tokenid, voutPubkeysEmpty /*=never spent*/, vopretFinish /*=non-fungible*/, opretRegister)); + EncodeTokenOpRet(tokenid, voutPubkeysEmpty /*=never spent*/, opretRegister)); } } } @@ -788,6 +798,8 @@ UniValue rogue_finishgame(uint64_t txfee,struct CCcontract_info *cp,cJSON *param // get any playerdata, get all keystrokes, replay game and compare final state CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); UniValue result(UniValue::VOBJ); std::string rawtx; CTransaction gametx; uint64_t seed,mult; int64_t buyin,batonvalue,inputsum,cashout,CCchange=0; int32_t i,err,gameheight,tmp,numplayers,regslot,n,num,numkeys,maxplayers,batonht,batonvout; char myrogueaddr[64],*keystrokes = 0; std::vector playerdata,newdata; uint256 batontxid,playertxid,gametxid; CPubKey mypk,roguepk; uint8_t player[10000],mypriv[32],funcid; + struct CCcontract_info *cpTokens, tokensC; + if ( txfee == 0 ) txfee = 10000; mypk = pubkey2pk(Mypubkey()); @@ -870,6 +882,9 @@ UniValue rogue_finishgame(uint64_t txfee,struct CCcontract_info *cp,cJSON *param } } mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange + (batonvalue-2*txfee),roguepk)); + cpTokens = CCinit(&tokensC, EVAL_TOKENS); + mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, txfee, GetUnspendable(cpTokens, NULL))); // marker to token cc addr, burnable and validated + Myprivkey(mypriv); CCaddr1of2set(cp,roguepk,mypk,mypriv,myrogueaddr); CScript opret = rogue_highlanderopret(funcid, gametxid, regslot,mypk, newdata); From 08be0457e6619121959b9d60cbcf54e3e3a9fdff Mon Sep 17 00:00:00 2001 From: dimxy Date: Mon, 11 Feb 2019 20:04:50 +0500 Subject: [PATCH 2/2] corr call to DecodeTokenOpRet --- src/cc/CCtokens.cpp | 36 +++++++++++++++++++++++++++++------- src/cc/rogue_rpc.cpp | 6 +++--- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/cc/CCtokens.cpp b/src/cc/CCtokens.cpp index f5eda7d31..eb802b65d 100644 --- a/src/cc/CCtokens.cpp +++ b/src/cc/CCtokens.cpp @@ -125,7 +125,7 @@ uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey,std::vector // we suppose in 'c' opret it might be only non-fungible payload and not any assets/heir/etc payloads if (!ss.eof()) { \ ss >> fieldId; \ - if( fieldId == OPRETID_NONFUNGIBLEDATA) \ + if( fieldId == OPRETID_NONFUNGIBLEDATA ) \ ss >> vopretNonfungible; \ })) return(funcid); @@ -153,6 +153,9 @@ uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey,std::vector return funcId; } */ +// decodes token opret: +// for 't' returns all data from opret, vopretExtra contains other contract's data (currently only assets'). +// for 'c' returns only funcid. NOTE: nonfungible data is not returned uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCodeTokens, uint256 &tokenid, std::vector &voutPubkeys, std::vector &vopretExtra) { std::vector vopret, extra, dummyPubkey, vnonfungibleDummy; @@ -163,6 +166,7 @@ uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCodeTokens, ui GetOpReturnData(scriptPubKey, vopret); script = (uint8_t *)vopret.data(); tokenid = zeroid; + vopretExtra.clear(); if (script != NULL && vopret.size() > 2) { @@ -174,7 +178,7 @@ uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCodeTokens, ui return (uint8_t)0; funcId = script[1]; - LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << "DecodeTokenOpRet decoded funcId=" << (char)(funcId?funcId:' ')); + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "DecodeTokenOpRet decoded funcId=" << (char)(funcId?funcId:' ')); switch( funcId ) { @@ -407,6 +411,20 @@ uint8_t ValidateTokenOpret(CTransaction tx, uint256 tokenid) { return (uint8_t)0; } +// remove token->unspendablePk (it is only for marker usage) +std::vector FilterOutTokensUnspendablePk(std::vector sourcePubkeys) { + struct CCcontract_info *cpTokens, tokensC; + cpTokens = CCinit(&tokensC, EVAL_TOKENS); + CPubKey tokensUnspendablePk = GetUnspendable(cpTokens, NULL); + std::vector destPubkeys; + + for (auto pk : sourcePubkeys) + if (pk != tokensUnspendablePk) + destPubkeys.push_back(pk); + + return destPubkeys; +} + // Checks if the vout is a really Tokens CC vout // also checks tokenid in opret or txid if this is 'c' tx // goDeeper is true: the func also validates amounts of the passed transaction: @@ -457,7 +475,7 @@ int64_t IsTokensvout(bool goDeeper, bool checkPubkeys /*<--not used, always true uint8_t dummyEvalCode; uint256 tokenIdOpret; - std::vector voutPubkeys; + std::vector voutPubkeys, voutPubkeysInOpret; std::vector vopretExtra, vopretNonfungible; uint8_t evalCode = EVAL_TOKENS; // if both payloads are empty maybe it is a transfer to non-payload-one-eval-token vout like GatewaysClaim @@ -466,11 +484,13 @@ int64_t IsTokensvout(bool goDeeper, bool checkPubkeys /*<--not used, always true // test vouts for possible token use-cases: std::vector> testVouts; - DecodeTokenOpRet(tx.vout.back().scriptPubKey, dummyEvalCode, tokenIdOpret, voutPubkeys, vopretExtra); + DecodeTokenOpRet(tx.vout.back().scriptPubKey, dummyEvalCode, tokenIdOpret, voutPubkeysInOpret, vopretExtra); LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "IsTokensvout() vopretExtra=" << HexStr(vopretExtra) << std::endl); GetNonfungibleData(reftokenid, vopretNonfungible); + voutPubkeys = FilterOutTokensUnspendablePk(voutPubkeysInOpret); + // NOTE: evalcode order in vouts is important: // non-fungible-eval -> EVAL_TOKENS -> assets-eval @@ -529,8 +549,10 @@ int64_t IsTokensvout(bool goDeeper, bool checkPubkeys /*<--not used, always true } // maybe it is single-eval or dual/three-eval token change? - std::vector vinPubkeys; - ExtractTokensVinPubkeys(tx, vinPubkeys); + std::vector vinPubkeys, vinPubkeysUnfiltered; + ExtractTokensVinPubkeys(tx, vinPubkeysUnfiltered); + vinPubkeys = FilterOutTokensUnspendablePk(vinPubkeysUnfiltered); + for(std::vector::iterator it = vinPubkeys.begin(); it != vinPubkeys.end(); it++) { testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, *it), std::string("single-eval cc1 self vin pk"))); testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode, evalCode2, tx.vout[v].nValue, *it), std::string("three-eval cc1 self vin pk"))); @@ -805,7 +827,7 @@ std::string CreateToken(int64_t txfee, int64_t tokensupply, std::string name, st mtx.vout.push_back(MakeTokensCC1vout(destEvalCode, tokensupply, mypk)); //mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(cp->CChexstr) << OP_CHECKSIG)); // old marker (non-burnable because spending could not be validated) // NOTE: we should prevent spending fake-tokens from this marker in IsTokenvout(): - mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, txfee, GetUnspendable(cp, NULL))); // new marker to cc addr, burnable and validated + mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, txfee, GetUnspendable(cp, NULL))); // new marker to token cc addr, burnable and validated return(FinalizeCCTx(0, cp, mtx, mypk, txfee, EncodeTokenCreateOpRet('c', Mypubkey(), name, description, nonfungibleData))); } diff --git a/src/cc/rogue_rpc.cpp b/src/cc/rogue_rpc.cpp index 5a1ef60b5..603e776b8 100644 --- a/src/cc/rogue_rpc.cpp +++ b/src/cc/rogue_rpc.cpp @@ -218,7 +218,7 @@ uint8_t rogue_registeropretdecode(uint256 &gametxid,uint256 &tokenid,uint256 &pl { return(f); } - else if ( (f= DecodeTokenOpRet(scriptPubKey,e,tokenid,voutPubkeys,vopret,vopret2)) == 'c' || f == 't' ) + else if ( (f= DecodeTokenOpRet(scriptPubKey,e,tokenid,voutPubkeys,vopret)) == 'c' || f == 't' ) { //fprintf(stderr,"got valid token opret %s e.%d vs %d\n",tokenid.GetHex().c_str(),e,EVAL_TOKENS); if ( vopret2.size() > 2 && E_UNMARSHAL(vopret2,ss >> e; ss >> f; ss >> gametxid; ss >> playertxid) != 0 && e == EVAL_ROGUE && f == 'R' ) @@ -731,7 +731,7 @@ UniValue rogue_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) if ( funcid == 'c' ) tokenid = playertxid; rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, - EncodeTokenOpRet(tokenid, voutPubkeysEmpty /*=never spent*/, opretRegister)); + EncodeTokenOpRet(tokenid, voutPubkeysEmpty /*=never spent*/, opretRegister)); } } } @@ -881,7 +881,7 @@ UniValue rogue_finishgame(uint64_t txfee,struct CCcontract_info *cp,cJSON *param } } } - mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange + (batonvalue-2*txfee),roguepk)); + mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange + (batonvalue-3*txfee),roguepk)); cpTokens = CCinit(&tokensC, EVAL_TOKENS); mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, txfee, GetUnspendable(cpTokens, NULL))); // marker to token cc addr, burnable and validated