diff --git a/src/cc/CCassets.h b/src/cc/CCassets.h index 69fcfacbb..c4623ccbb 100644 --- a/src/cc/CCassets.h +++ b/src/cc/CCassets.h @@ -29,11 +29,8 @@ bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); // CCassetsCore -//CTxOut MakeAssetsVout(CAmount nValue,CPubKey pk); -//CScript EncodeAssetCreateOpRet(uint8_t funcid,std::vector origpubkey,std::string name,std::string description); -//CScript EncodeAssetOpRet(uint8_t assetFuncId, uint256 tokenid, uint256 assetid2, int64_t price, std::vector origpubkey); -//bool DecodeAssetCreateOpRet(const CScript &scriptPubKey,std::vector &origpubkey,std::string &name,std::string &description); -//uint8_t DecodeAssetOpRet(const CScript &scriptPubKey, uint8_t &evalCode, uint256 &assetid, uint256 &assetid2, int64_t &price, std::vector &origpubkey); +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); 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 1b8e46189..f14de3956 100644 --- a/src/cc/CCassetsCore.cpp +++ b/src/cc/CCassetsCore.cpp @@ -43,17 +43,17 @@ bool ValidateBidRemainder(int64_t remaining_units,int64_t remaining_nValue,int64 int64_t unitprice,recvunitprice,newunitprice=0; if ( orig_nValue == 0 || received_nValue == 0 || paidunits == 0 || totalunits == 0 ) { - fprintf(stderr,"ValidateAssetRemainder: orig_nValue == %llu || received_nValue == %llu || paidunits == %llu || totalunits == %llu\n",(long long)orig_nValue,(long long)received_nValue,(long long)paidunits,(long long)totalunits); + fprintf(stderr,"ValidateAssetRemainder() orig_nValue == %llu || received_nValue == %llu || paidunits == %llu || totalunits == %llu\n",(long long)orig_nValue,(long long)received_nValue,(long long)paidunits,(long long)totalunits); return(false); } else if ( totalunits != (remaining_units + paidunits) ) { - fprintf(stderr,"ValidateAssetRemainder: totalunits %llu != %llu (remaining_units %llu + %llu paidunits)\n",(long long)totalunits,(long long)(remaining_units + paidunits),(long long)remaining_units,(long long)paidunits); + fprintf(stderr,"ValidateAssetRemainder() totalunits %llu != %llu (remaining_units %llu + %llu paidunits)\n",(long long)totalunits,(long long)(remaining_units + paidunits),(long long)remaining_units,(long long)paidunits); return(false); } else if ( orig_nValue != (remaining_nValue + received_nValue) ) { - fprintf(stderr,"ValidateAssetRemainder: orig_nValue %llu != %llu (remaining_nValue %llu + %llu received_nValue)\n",(long long)orig_nValue,(long long)(remaining_nValue - received_nValue),(long long)remaining_nValue,(long long)received_nValue); + fprintf(stderr,"ValidateAssetRemainder() orig_nValue %llu != %llu (remaining_nValue %llu + %llu received_nValue)\n",(long long)orig_nValue,(long long)(remaining_nValue - received_nValue),(long long)remaining_nValue,(long long)received_nValue); return(false); } else @@ -68,10 +68,10 @@ bool ValidateBidRemainder(int64_t remaining_units,int64_t remaining_nValue,int64 newunitprice = (remaining_nValue / remaining_units); if ( recvunitprice < unitprice ) { - fprintf(stderr,"error recvunitprice %.8f < %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/(COIN),(double)unitprice/(COIN),(double)newunitprice/(COIN)); + fprintf(stderr,"ValidateAssetRemainder() error recvunitprice %.8f < %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/(COIN),(double)unitprice/(COIN),(double)newunitprice/(COIN)); return(false); } - fprintf(stderr,"orig %llu total %llu, recv %llu paid %llu,recvunitprice %.8f >= %.8f unitprice, new unitprice %.8f\n",(long long)orig_nValue,(long long)totalunits,(long long)received_nValue,(long long)paidunits,(double)recvunitprice/(COIN),(double)unitprice/(COIN),(double)newunitprice/(COIN)); + fprintf(stderr,"ValidateAssetRemainder() orig %llu total %llu, recv %llu paid %llu,recvunitprice %.8f >= %.8f unitprice, new unitprice %.8f\n",(long long)orig_nValue,(long long)totalunits,(long long)received_nValue,(long long)paidunits,(double)recvunitprice/(COIN),(double)unitprice/(COIN),(double)newunitprice/(COIN)); } return(true); } @@ -89,7 +89,7 @@ bool SetBidFillamounts(int64_t &received_nValue,int64_t &remaining_units,int64_t paidunits = totalunits; received_nValue = orig_nValue; remaining_units = 0; - fprintf(stderr,"totally filled!\n"); + fprintf(stderr,"SetBidFillamounts() bid order totally filled!\n"); return(true); } remaining_units = (totalunits - paidunits); @@ -100,7 +100,7 @@ bool SetBidFillamounts(int64_t &received_nValue,int64_t &remaining_units,int64_t if ( unitprice > 0 && received_nValue > 0 && received_nValue <= orig_nValue ) { remaining_nValue = (orig_nValue - received_nValue); - printf("total.%llu - paid.%llu, remaining %llu <- %llu (%llu - %llu)\n",(long long)totalunits,(long long)paidunits,(long long)remaining_nValue,(long long)(orig_nValue - received_nValue),(long long)orig_nValue,(long long)received_nValue); + printf("SetBidFillamounts() total.%llu - paid.%llu, remaining %llu <- %llu (%llu - %llu)\n",(long long)totalunits,(long long)paidunits,(long long)remaining_nValue,(long long)(orig_nValue - received_nValue),(long long)orig_nValue,(long long)received_nValue); return(ValidateBidRemainder(remaining_units,remaining_nValue,orig_nValue,received_nValue,paidunits,totalunits)); } else return(false); } @@ -118,14 +118,14 @@ bool SetAskFillamounts(int64_t &received_assetoshis,int64_t &remaining_nValue,in paid_nValue = total_nValue; received_assetoshis = orig_assetoshis; remaining_nValue = 0; - fprintf(stderr,"totally filled!\n"); + fprintf(stderr,"SetAskFillamounts() ask order totally filled!\n"); return(true); } remaining_nValue = (total_nValue - paid_nValue); dunitprice = ((double)total_nValue / orig_assetoshis); received_assetoshis = (paid_nValue / dunitprice); - fprintf(stderr,"remaining_nValue %.8f (%.8f - %.8f)\n",(double)remaining_nValue/COIN,(double)total_nValue/COIN,(double)paid_nValue/COIN); - fprintf(stderr,"unitprice %.8f received_assetoshis %llu orig %llu\n",dunitprice/COIN,(long long)received_assetoshis,(long long)orig_assetoshis); + fprintf(stderr,"SetAskFillamounts() remaining_nValue %.8f (%.8f - %.8f)\n",(double)remaining_nValue/COIN,(double)total_nValue/COIN,(double)paid_nValue/COIN); + fprintf(stderr,"SetAskFillamounts() unitprice %.8f received_assetoshis %llu orig %llu\n",dunitprice/COIN,(long long)received_assetoshis,(long long)orig_assetoshis); if ( fabs(dunitprice) > SMALLVAL && received_assetoshis > 0 && received_assetoshis <= orig_assetoshis ) { remaining_assetoshis = (orig_assetoshis - received_assetoshis); @@ -138,17 +138,17 @@ bool ValidateAskRemainder(int64_t remaining_nValue,int64_t remaining_assetoshis, int64_t unitprice,recvunitprice,newunitprice=0; if ( orig_assetoshis == 0 || received_assetoshis == 0 || paid_nValue == 0 || total_nValue == 0 ) { - fprintf(stderr,"ValidateAssetRemainder: orig_assetoshis == %llu || received_assetoshis == %llu || paid_nValue == %llu || total_nValue == %llu\n",(long long)orig_assetoshis,(long long)received_assetoshis,(long long)paid_nValue,(long long)total_nValue); + fprintf(stderr,"ValidateAssetRemainder() orig_assetoshis == %llu || received_assetoshis == %llu || paid_nValue == %llu || total_nValue == %llu\n",(long long)orig_assetoshis,(long long)received_assetoshis,(long long)paid_nValue,(long long)total_nValue); return(false); } else if ( total_nValue != (remaining_nValue + paid_nValue) ) { - fprintf(stderr,"ValidateAssetRemainder: total_nValue %llu != %llu (remaining_nValue %llu + %llu paid_nValue)\n",(long long)total_nValue,(long long)(remaining_nValue + paid_nValue),(long long)remaining_nValue,(long long)paid_nValue); + fprintf(stderr,"ValidateAssetRemainder() total_nValue %llu != %llu (remaining_nValue %llu + %llu paid_nValue)\n",(long long)total_nValue,(long long)(remaining_nValue + paid_nValue),(long long)remaining_nValue,(long long)paid_nValue); return(false); } else if ( orig_assetoshis != (remaining_assetoshis + received_assetoshis) ) { - fprintf(stderr,"ValidateAssetRemainder: orig_assetoshis %llu != %llu (remaining_nValue %llu + %llu received_nValue)\n",(long long)orig_assetoshis,(long long)(remaining_assetoshis - received_assetoshis),(long long)remaining_assetoshis,(long long)received_assetoshis); + fprintf(stderr,"ValidateAssetRemainder() orig_assetoshis %llu != %llu (remaining_nValue %llu + %llu received_nValue)\n",(long long)orig_assetoshis,(long long)(remaining_assetoshis - received_assetoshis),(long long)remaining_assetoshis,(long long)received_assetoshis); return(false); } else @@ -159,10 +159,10 @@ bool ValidateAskRemainder(int64_t remaining_nValue,int64_t remaining_assetoshis, newunitprice = (remaining_nValue / remaining_assetoshis); if ( recvunitprice < unitprice ) { - fprintf(stderr,"error recvunitprice %.8f < %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/COIN,(double)unitprice/COIN,(double)newunitprice/COIN); + fprintf(stderr,"ValidateAskRemainder() error recvunitprice %.8f < %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/COIN,(double)unitprice/COIN,(double)newunitprice/COIN); return(false); } - fprintf(stderr,"got recvunitprice %.8f >= %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/COIN,(double)unitprice/COIN,(double)newunitprice/COIN); + fprintf(stderr,"ValidateAskRemainder() got recvunitprice %.8f >= %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/COIN,(double)unitprice/COIN,(double)newunitprice/COIN); } return(true); } @@ -172,7 +172,7 @@ bool SetSwapFillamounts(int64_t &received_assetoshis,int64_t &remaining_assetosh int64_t remaining_assetoshis; double dunitprice; if ( total_assetoshis2 == 0 ) { - fprintf(stderr,"total_assetoshis2.0 origsatoshis.%llu paid_assetoshis2.%llu\n",(long long)orig_assetoshis,(long long)paid_assetoshis2); + fprintf(stderr,"SetSwapFillamounts() total_assetoshis2.0 origsatoshis.%llu paid_assetoshis2.%llu\n",(long long)orig_assetoshis,(long long)paid_assetoshis2); received_assetoshis = remaining_assetoshis2 = paid_assetoshis2 = 0; return(false); } @@ -181,14 +181,14 @@ bool SetSwapFillamounts(int64_t &received_assetoshis,int64_t &remaining_assetosh paid_assetoshis2 = total_assetoshis2; received_assetoshis = orig_assetoshis; remaining_assetoshis2 = 0; - fprintf(stderr,"totally filled!\n"); + fprintf(stderr,"SetSwapFillamounts() swap order totally filled!\n"); return(true); } remaining_assetoshis2 = (total_assetoshis2 - paid_assetoshis2); dunitprice = ((double)total_assetoshis2 / orig_assetoshis); received_assetoshis = (paid_assetoshis2 / dunitprice); - fprintf(stderr,"remaining_assetoshis2 %llu (%llu - %llu)\n",(long long)remaining_assetoshis2/COIN,(long long)total_assetoshis2/COIN,(long long)paid_assetoshis2/COIN); - fprintf(stderr,"unitprice %.8f received_assetoshis %llu orig %llu\n",dunitprice/COIN,(long long)received_assetoshis,(long long)orig_assetoshis); + fprintf(stderr,"SetSwapFillamounts() remaining_assetoshis2 %llu (%llu - %llu)\n",(long long)remaining_assetoshis2/COIN,(long long)total_assetoshis2/COIN,(long long)paid_assetoshis2/COIN); + fprintf(stderr,"SetSwapFillamounts() unitprice %.8f received_assetoshis %llu orig %llu\n",dunitprice/COIN,(long long)received_assetoshis,(long long)orig_assetoshis); if ( fabs(dunitprice) > SMALLVAL && received_assetoshis > 0 && received_assetoshis <= orig_assetoshis ) { remaining_assetoshis = (orig_assetoshis - received_assetoshis); @@ -201,17 +201,17 @@ bool ValidateSwapRemainder(int64_t remaining_price,int64_t remaining_nValue,int6 int64_t unitprice,recvunitprice,newunitprice=0; if ( orig_nValue == 0 || received_nValue == 0 || paidunits == 0 || totalunits == 0 ) { - fprintf(stderr,"ValidateAssetRemainder: orig_nValue == %llu || received_nValue == %llu || paidunits == %llu || totalunits == %llu\n",(long long)orig_nValue,(long long)received_nValue,(long long)paidunits,(long long)totalunits); + fprintf(stderr,"ValidateAssetRemainder() orig_nValue == %llu || received_nValue == %llu || paidunits == %llu || totalunits == %llu\n",(long long)orig_nValue,(long long)received_nValue,(long long)paidunits,(long long)totalunits); return(false); } else if ( totalunits != (remaining_price + paidunits) ) { - fprintf(stderr,"ValidateAssetRemainder: totalunits %llu != %llu (remaining_price %llu + %llu paidunits)\n",(long long)totalunits,(long long)(remaining_price + paidunits),(long long)remaining_price,(long long)paidunits); + fprintf(stderr,"ValidateAssetRemainder() totalunits %llu != %llu (remaining_price %llu + %llu paidunits)\n",(long long)totalunits,(long long)(remaining_price + paidunits),(long long)remaining_price,(long long)paidunits); return(false); } else if ( orig_nValue != (remaining_nValue + received_nValue) ) { - fprintf(stderr,"ValidateAssetRemainder: orig_nValue %llu != %llu (remaining_nValue %llu + %llu received_nValue)\n",(long long)orig_nValue,(long long)(remaining_nValue - received_nValue),(long long)remaining_nValue,(long long)received_nValue); + fprintf(stderr,"ValidateAssetRemainder() orig_nValue %llu != %llu (remaining_nValue %llu + %llu received_nValue)\n",(long long)orig_nValue,(long long)(remaining_nValue - received_nValue),(long long)remaining_nValue,(long long)received_nValue); return(false); } else @@ -222,10 +222,10 @@ bool ValidateSwapRemainder(int64_t remaining_price,int64_t remaining_nValue,int6 newunitprice = (remaining_nValue * COIN) / remaining_price; if ( recvunitprice < unitprice ) { - fprintf(stderr,"error recvunitprice %.8f < %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/(COIN*COIN),(double)unitprice/(COIN*COIN),(double)newunitprice/(COIN*COIN)); + fprintf(stderr,"ValidateAssetRemainder() error recvunitprice %.8f < %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/(COIN*COIN),(double)unitprice/(COIN*COIN),(double)newunitprice/(COIN*COIN)); return(false); } - fprintf(stderr,"recvunitprice %.8f >= %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/(COIN*COIN),(double)unitprice/(COIN*COIN),(double)newunitprice/(COIN*COIN)); + fprintf(stderr,"ValidateAssetRemainder() recvunitprice %.8f >= %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/(COIN*COIN),(double)unitprice/(COIN*COIN),(double)newunitprice/(COIN*COIN)); } return(true); } @@ -279,19 +279,35 @@ bool DecodeAssetCreateOpRet(const CScript &scriptPubKey, std::vector &o return(0); } */ -uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &evalCodeInOpret, uint256 &tokenid, 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) { - std::vector vopretExtra, vopretStripped; - uint8_t *script, funcId = 0, assetFuncId = 0, dummyEvalCode, dummyAssetFuncId; + std::vector vopret1, vopret2; + std::vector vopretAssets; //, vopretAssetsStripped; + uint8_t *script, funcId = 0, assetsFuncId = 0, dummyEvalCode, dummyAssetFuncId; uint256 dummyTokenid; std::vector voutPubkeysDummy; tokenid = zeroid; assetid2 = zeroid; price = 0; + assetsEvalCode = 0; + assetsFuncId = 0; // First - decode token opret: - funcId = DecodeTokenOpRet(scriptPubKey, evalCodeInOpret, tokenid, voutPubkeysDummy, vopretExtra); + funcId = DecodeTokenOpRet(scriptPubKey, dummyEvalCode, tokenid, voutPubkeysDummy, vopret1, vopret2); + std::cerr << "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(); @@ -302,54 +318,59 @@ uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &evalCodeInOp //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 || vopretExtra.size() < 2) { - std::cerr << "DecodeAssetOpRet() incorrect opret or no asset's payload" << " funcId=" << (int)funcId << " vopretExtra.size()=" << vopretExtra.size() << std::endl; + if (funcId == 0 || vopretAssets.size() < 2) { + std::cerr << "DecodeAssetTokenOpRet() incorrect opret or no asset's payload" << " funcId=" << (int)funcId << " vopretAssets.size()=" << vopretAssets.size() << std::endl; return (uint8_t)0; } - if (!E_UNMARSHAL(vopretExtra, { ss >> vopretStripped; })) { //strip string size - std::cerr << "DecodeAssetTokenOpRet() could not unmarshal vopretStripped" << std::endl; - return (uint8_t)0; - } + //if (!E_UNMARSHAL(vopretAssets, { ss >> vopretAssetsStripped; })) { //strip string size + // std::cerr << "DecodeAssetTokenOpRet() could not unmarshal vopretAssetsStripped" << std::endl; + // return (uint8_t)0; + //} - ////tokenid = revuint256(tokenid); already done in DecodeToken! - evalCodeInOpret = vopretStripped.begin()[0]; - assetFuncId = vopretStripped.begin()[1]; + // additional check to prevent crash + if (vopretAssets.size() >= 2) { - //std::cerr << "DecodeAssetOpRet() evalCodeInOpret=" << (int)evalCodeInOpret << " funcId=" << (char)(funcId ? funcId : ' ') << " assetFuncId=" << (char)(assetFuncId ? assetFuncId : ' ') << std::endl; + assetsEvalCode = vopretAssets.begin()[0]; + assetsFuncId = vopretAssets.begin()[1]; - if(evalCodeInOpret == EVAL_ASSETS) - { - //fprintf(stderr,"decode.[%c] assetFuncId.[%c]\n", funcId, assetFuncId); - switch( assetFuncId ) + //std::cerr << "DecodeAssetTokenOpRet() evalCodeInOpret=" << (int)evalCodeInOpret << " funcId=" << (char)(funcId ? funcId : ' ') << " assetFuncId=" << (char)(assetFuncId ? assetFuncId : ' ') << std::endl; + + if (assetsEvalCode == EVAL_ASSETS) { - case 'x': case 'o': - if (vopretStripped.size() == 2) // no data after 'evalcode assetFuncId' allowed + //fprintf(stderr,"DecodeAssetTokenOpRet() decode.[%c] assetFuncId.[%c]\n", funcId, assetFuncId); + switch (assetsFuncId) + { + case 'x': case 'o': + if (vopretAssets.size() == 2) // no data after 'evalcode assetFuncId' allowed { - return(assetFuncId); + return(assetsFuncId); } break; case 's': case 'b': case 'S': case 'B': - if (E_UNMARSHAL(vopretStripped, ss >> dummyEvalCode; ss >> dummyAssetFuncId; ss >> price; ss >> origpubkey) != 0) + if (E_UNMARSHAL(vopretAssets, ss >> dummyEvalCode; ss >> dummyAssetFuncId; ss >> price; ss >> origpubkey) != 0) { - //fprintf(stderr,"DecodeAssetTokenOpRet got price %llu\n",(long long)price); - return(assetFuncId); + //fprintf(stderr,"DecodeAssetTokenOpRet() got price %llu\n",(long long)price); + return(assetsFuncId); } break; case 'E': case 'e': - if ( E_UNMARSHAL(vopretStripped, ss >> dummyEvalCode; ss >> dummyAssetFuncId; ss >> assetid2; ss >> price; ss >> origpubkey) != 0 ) + if (E_UNMARSHAL(vopretAssets, ss >> dummyEvalCode; ss >> dummyAssetFuncId; ss >> assetid2; ss >> price; ss >> origpubkey) != 0) { - //fprintf(stderr,"DecodeAssetTokenOpRet got price %llu\n",(long long)price); + //fprintf(stderr,"DecodeAssetTokenOpRet() got price %llu\n",(long long)price); assetid2 = revuint256(assetid2); - return(assetFuncId); + return(assetsFuncId); } break; default: - fprintf(stderr,"DecodeAssetTokenOpRet: illegal assetFuncId.%02x\n", assetFuncId); + fprintf(stderr, "DecodeAssetTokenOpRet() illegal assetFuncId.%02x\n", assetsFuncId); //funcId = 0; break; + } } } + + std::cerr << "DecodeAssetTokenOpRet() no asset's payload or incorrect assets evalcode" << " funcId=" << (int)funcId << " vopretAssets.size()=" << vopretAssets.size() << " assetsEvalCode=" << assetsEvalCode << " assetsFuncId=" << assetsFuncId << std::endl; return (uint8_t)0; } @@ -358,49 +379,55 @@ bool SetAssetOrigpubkey(std::vector &origpubkey,int64_t &price,const CT { uint256 assetid,assetid2; uint8_t evalCode; - if ( tx.vout.size() > 0 && DecodeAssetTokenOpRet(tx.vout[tx.vout.size()-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey) != 0 ) + std::vector vopretNonfungibleDummy; + + if ( tx.vout.size() > 0 && DecodeAssetTokenOpRet(tx.vout[tx.vout.size()-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey, vopretNonfungibleDummy) != 0 ) return(true); else return(false); } -// Calculate sell/buy owner's source token/asset address from ask/bid tx -bool GetAssetorigaddrs(struct CCcontract_info *cp, char *userCCaddr, char *destaddr, const CTransaction& tx) +// Calculate seller/buyer's dest cc address from ask/bid tx funcid +bool GetAssetorigaddrs(struct CCcontract_info *cp, char *origCCaddr, char *origNormalAddr, const CTransaction& vintx) { - uint256 assetid,assetid2; int64_t price,nValue=0; int32_t n; uint8_t funcid; + uint256 assetid, assetid2; + int64_t price,nValue=0; + int32_t n; + uint8_t vintxFuncId; std::vector origpubkey; CScript script; uint8_t evalCode; + std::vector vopretNonfungibleDummy; - n = tx.vout.size(); - if( n == 0 || (funcid = DecodeAssetTokenOpRet(tx.vout[n-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey)) == 0 ) + n = vintx.vout.size(); + if( n == 0 || (vintxFuncId = DecodeAssetTokenOpRet(vintx.vout[n-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey, vopretNonfungibleDummy)) == 0 ) return(false); bool bGetCCaddr = false; - if (funcid == 's' || funcid == 'S') { - struct CCcontract_info *cpTokens, tokensC; - cpTokens = CCinit(&tokensC, EVAL_TOKENS); - bGetCCaddr = GetCCaddress(cpTokens, userCCaddr, pubkey2pk(origpubkey)); - //bGetCCaddr = GetTokensCCaddress(cp, CCaddr, pubkey2pk(origpubkey)); + struct CCcontract_info *cpTokens, tokensC; + cpTokens = CCinit(&tokensC, EVAL_TOKENS); + + if (vintxFuncId == 's' || vintxFuncId == 'S') { + // bGetCCaddr = GetCCaddress(cpTokens, origCCaddr, pubkey2pk(origpubkey)); + cpTokens->additionalTokensEvalcode2 = cp->additionalTokensEvalcode2; // add non-fungible if present + bGetCCaddr = GetTokensCCaddress(cpTokens, origCCaddr, pubkey2pk(origpubkey)); // tokens to single-eval token or token+nonfungible } - else if (funcid == 'b' || funcid == 'B') { - struct CCcontract_info *cpTokens, tokensC; - cpTokens = CCinit(&tokensC, EVAL_TOKENS); - bGetCCaddr = GetCCaddress(cpTokens, userCCaddr, pubkey2pk(origpubkey)); + else if (vintxFuncId == 'b' || vintxFuncId == 'B') { + cpTokens->additionalTokensEvalcode2 = cp->additionalTokensEvalcode2; // add non-fungible if present + bGetCCaddr = GetTokensCCaddress(cpTokens, origCCaddr, pubkey2pk(origpubkey)); // tokens to single-eval token or token+nonfungible } else { - std::cerr << "GetAssetorigaddrs incorrect funcid=" << (char)(funcid?funcid:' ') << std::endl; + std::cerr << "GetAssetorigaddrs incorrect vintx funcid=" << (char)(vintxFuncId?vintxFuncId:' ') << std::endl; return false; } - - if( bGetCCaddr && Getscriptaddress(destaddr, CScript() << origpubkey << OP_CHECKSIG)) + if( bGetCCaddr && Getscriptaddress(origNormalAddr, CScript() << origpubkey << OP_CHECKSIG)) return(true); else return(false); } -int64_t AssetValidateCCvin(struct CCcontract_info *cp,Eval* eval,char *CCaddr,char *origaddr,const CTransaction &tx,int32_t vini,CTransaction &vinTx) +int64_t AssetValidateCCvin(struct CCcontract_info *cp,Eval* eval,char *origCCaddr,char *origaddr,const CTransaction &tx,int32_t vini,CTransaction &vinTx) { uint256 hashBlock; uint256 assetid, assetid2; @@ -410,7 +437,7 @@ int64_t AssetValidateCCvin(struct CCcontract_info *cp,Eval* eval,char *CCaddr,ch char destaddr[64], unspendableAddr[64]; - origaddr[0] = destaddr[0] = CCaddr[0] = 0; + origaddr[0] = destaddr[0] = origCCaddr[0] = 0; uint8_t funcid = 0; if (tx.vout.size() > 0) { @@ -418,7 +445,9 @@ int64_t AssetValidateCCvin(struct CCcontract_info *cp,Eval* eval,char *CCaddr,ch int64_t tmpprice; std::vector tmporigpubkey; uint8_t evalCode; - funcid = DecodeAssetTokenOpRet(tx.vout[tx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, tmpprice, tmporigpubkey); + std::vector vopretNonfungibleDummy; + + funcid = DecodeAssetTokenOpRet(tx.vout[tx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, tmpprice, tmporigpubkey, vopretNonfungibleDummy); } if( tx.vin.size() < 2 ) @@ -427,45 +456,48 @@ int64_t AssetValidateCCvin(struct CCcontract_info *cp,Eval* eval,char *CCaddr,ch return eval->Invalid("vin1 needs to be buyvin.vout[0]"); else if( eval->GetTxUnconfirmed(tx.vin[vini].prevout.hash, vinTx,hashBlock) == 0 ) { - /* int32_t z; - for (z=31; z>=0; z--) - fprintf(stderr,"%02x",((uint8_t *)&tx.vin[vini].prevout.hash)[z]); - fprintf(stderr," vini.%d\n",vini); */ - std::cerr << "AssetValidateCCvin cannot load vintx for vin=" << vini << " vintx id=" << tx.vin[vini].prevout.hash.GetHex() << std::endl; + std::cerr << "AssetValidateCCvin() cannot load vintx for vin=" << vini << " vintx id=" << tx.vin[vini].prevout.hash.GetHex() << std::endl; return eval->Invalid("always should find CCvin, but didnt"); } - // if fillSell or cancelSell --> to spend tokens from dual-eval token-assets unspendable addr + // check source cc unspendable cc address: + // if fillSell or cancelSell --> should spend tokens from dual-eval token-assets unspendable addr else if( (funcid == 'S' || funcid == 'x') && (Getscriptaddress(destaddr, vinTx.vout[tx.vin[vini].prevout.n].scriptPubKey) == 0 || !GetTokensCCaddress(cp, unspendableAddr, GetUnspendable(cp, NULL)) || strcmp(destaddr, unspendableAddr) != 0)) { - fprintf(stderr,"AssetValidateCCvin cc addr %s is not dual token-evalcode=0x%02x asset unspendable addr %s\n", destaddr, (int)cp->evalcode, unspendableAddr); - return eval->Invalid("invalid vin AssetsCCaddr"); + fprintf(stderr,"AssetValidateCCvin() cc addr %s is not dual token-evalcode=0x%02x asset unspendable addr %s\n", destaddr, (int)cp->evalcode, unspendableAddr); + return eval->Invalid("invalid vin assets CCaddr"); } - // if fillBuy or cancelBuy --> to spend coins from asset unspendable addr + // if fillBuy or cancelBuy --> should spend coins from asset unspendable addr else if ((funcid == 'B' || funcid == 'o') && (Getscriptaddress(destaddr, vinTx.vout[tx.vin[vini].prevout.n].scriptPubKey) == 0 || !GetCCaddress(cp, unspendableAddr, GetUnspendable(cp, NULL)) || strcmp(destaddr, unspendableAddr) != 0)) { - fprintf(stderr, "AssetValidateCCvin cc addr %s is not evalcode=0x%02x asset unspendable addr %s\n", destaddr, (int)cp->evalcode, unspendableAddr); - return eval->Invalid("invalid vin AssetsCCaddr"); + fprintf(stderr, "AssetValidateCCvin() cc addr %s is not evalcode=0x%02x asset unspendable addr %s\n", destaddr, (int)cp->evalcode, unspendableAddr); + return eval->Invalid("invalid vin assets CCaddr"); } - + // end of check source unspendable cc address //else if ( vinTx.vout[0].nValue < 10000 ) // return eval->Invalid("invalid dust for buyvin"); - else if( GetAssetorigaddrs(cp, CCaddr, origaddr, vinTx) == 0 ) + // get user dest cc and normal addresses: + else if( GetAssetorigaddrs(cp, origCCaddr, origaddr, vinTx) == 0 ) return eval->Invalid("couldnt get origaddr for buyvin"); - fprintf(stderr,"AssetValidateCCvin got %.8f to origaddr.(%s)\n",(double)vinTx.vout[tx.vin[vini].prevout.n].nValue/COIN,origaddr); + + fprintf(stderr,"AssetValidateCCvin() got %.8f to origaddr.(%s)\n", (double)vinTx.vout[tx.vin[vini].prevout.n].nValue/COIN,origaddr); + if ( vinTx.vout[0].nValue == 0 ) return eval->Invalid("null value CCvin"); + return(vinTx.vout[0].nValue); } 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; // validate locked coins on Assets vin[1] @@ -473,7 +505,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)) == 'b' && + else if ((funcid = DecodeAssetTokenOpRet(vinTx.vout[vinTx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, tmpprice, tmporigpubkey, vopretNonfungibleDummy)) == 'b' && vinTx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 ) // marker is only in 'b'? return eval->Invalid("invalid normal vout1 for buyvin"); else @@ -493,7 +525,7 @@ int64_t AssetValidateBuyvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmppr int64_t AssetValidateSellvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmpprice,std::vector &tmporigpubkey,char *CCaddr,char *origaddr,const CTransaction &tx,uint256 assetid) { CTransaction vinTx; int64_t nValue,assetoshis; - //fprintf(stderr,"AssetValidateSellvin\n"); + //fprintf(stderr,"AssetValidateSellvin()\n"); if ( (nValue = AssetValidateCCvin(cp, eval, CCaddr, origaddr, tx, 1, vinTx)) == 0 ) return(0); if ( (assetoshis= IsAssetvout(cp, tmpprice, tmporigpubkey, vinTx, 0, assetid)) == 0 ) @@ -508,13 +540,14 @@ 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)) == 0) + if ((funcid = DecodeAssetTokenOpRet(tx.vout[n - 1].scriptPubKey, evalCode, assetidOpret, assetidOpret2, price, origpubkey, vopretNonfungibleDummy)) == 0) { - std::cerr << "ValidateAssetOpret() DecodeAssetTokenOpRet returned null for the opret for txid=" << tx.GetHash().GetHex() << std::endl; + std::cerr << "ValidateAssetOpret() DecodeAssetTokenOpRet returned funcId=0 for opret from txid=" << tx.GetHash().GetHex() << std::endl; return(false); } /* it is now on token level: @@ -556,22 +589,20 @@ bool ValidateAssetOpret(CTransaction tx, int32_t v, uint256 assetid, int64_t &pr } // Checks if the vout is a really Asset CC vout -// compareTotals == true, the func also validates the passed transaction itself: -// it should be either sum(cc vins) == sum(cc vouts) or the transaction is the 'tokenbase' ('c') tx int64_t IsAssetvout(struct CCcontract_info *cp, int64_t &price, std::vector &origpubkey, const CTransaction& tx, int32_t v, uint256 refassetid) { //std::cerr << "IsAssetvout() entered for txid=" << tx.GetHash().GetHex() << " v=" << v << " for assetid=" << refassetid.GetHex() << std::endl; + int32_t n = tx.vout.size(); + // just check boundaries: + if (v >= n - 1) { // just moved this up (dimxy) + std::cerr << "isAssetVout() internal err: (v >= n - 1), returning 0" << std::endl; + return(0); + } + if (tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0) // maybe check address too? dimxy: possibly no, because there are too many cases with different addresses here { - int32_t n = tx.vout.size(); - // just check boundaries: - if (v >= n - 1) { // just moved this up (dimxy) - std::cerr << "isAssetVout() internal err: (v >= n - 1), returning 0" << std::endl; - return(0); - } - // moved opret checking to this new reusable func (dimxy): const bool valOpret = ValidateAssetOpret(tx, v, refassetid, price, origpubkey); //std::cerr << "IsAssetvout() ValidateAssetOpret returned=" << std::boolalpha << valOpret << " for txid=" << tx.GetHash().GetHex() << " for assetid=" << refassetid.GetHex() << std::endl; @@ -627,12 +658,9 @@ bool AssetCalcAmounts(struct CCcontract_info *cpAssets, int64_t &inputs, int64_t } } - for (int32_t i = 0; i < numvouts; i++) + for (int32_t i = 0; i < numvouts-1; i++) { - // Note: we pass in here 'false' because we don't need to call AssetExactAmounts() recursively from IsAssetvout - // indeed, in this case we'll be checking this tx again assetoshis = IsAssetvout(cpAssets, tmpprice, tmporigpubkey, tx, i, assetid); - if (assetoshis != 0) { std::cerr << "AssetCalcAmounts() vout i=" << i << " assetoshis=" << assetoshis << std::endl; @@ -643,12 +671,6 @@ bool AssetCalcAmounts(struct CCcontract_info *cpAssets, int64_t &inputs, int64_t //std::cerr << "AssetCalcAmounts() inputs=" << inputs << " outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << std::endl; /* we do not verify inputs == outputs here, - it's done in Tokens: - if (inputs != outputs) { - if (tx.GetHash() != assetid) { - std::cerr << "AssetCalcAmounts() unequal inputs=" << inputs << " vs outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << std::endl; - return (!eval) ? false : eval->Invalid("assets cc inputs != cc outputs"); - } - } */ + it's now done in Tokens */ return(true); } diff --git a/src/cc/CCassetstx.cpp b/src/cc/CCassetstx.cpp index 9d83beb2c..c057237f9 100644 --- a/src/cc/CCassetstx.cpp +++ b/src/cc/CCassetstx.cpp @@ -14,9 +14,9 @@ ******************************************************************************/ #include "CCassets.h" -//#include "CCtokens.h" - +#include "CCtokens.h" +/* use AddTokenCCInputs instead int64_t AddAssetInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint256 assetid,int64_t total,int32_t maxinputs) { char coinaddr[64],destaddr[64]; int64_t threshold,nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t j,vout,n = 0; @@ -64,25 +64,19 @@ int64_t AddAssetInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubK //std::cerr << "AddAssetInputs() found totalinputs=" << totalinputs << std::endl; return(totalinputs); } - +*/ UniValue AssetOrders(uint256 refassetid) { static uint256 zero; UniValue result(UniValue::VARR); - std::vector > unspentOutputsTokens, unspentOutputsAssets; - - struct CCcontract_info *cpTokens, tokensC; - struct CCcontract_info *cpAssets, assetsC; - - cpTokens = CCinit(&tokensC, EVAL_TOKENS); - cpAssets = CCinit(&assetsC, EVAL_ASSETS); auto addOrders = [&](struct CCcontract_info *cp, std::vector >::const_iterator it) { 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]; @@ -93,7 +87,7 @@ UniValue AssetOrders(uint256 refassetid) { // 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)) != 0) + if (vintx.vout.size() > 0 && (funcid = DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey, vopretNonfungible)) != 0) { if (refassetid != zero && assetid != refassetid) { @@ -163,25 +157,35 @@ UniValue AssetOrders(uint256 refassetid) } }; + std::vector > unspentOutputsTokens, unspentOutputsCoins; + struct CCcontract_info *cpAssets, assetsC; + + cpAssets = CCinit(&assetsC, EVAL_ASSETS); char assetsUnspendableAddr[64]; GetCCaddress(cpAssets, assetsUnspendableAddr, GetUnspendable(cpAssets, NULL)); - SetCCunspents(unspentOutputsAssets, assetsUnspendableAddr /*(char *)cpTokens->unspendableCCaddr*/); + SetCCunspents(unspentOutputsCoins, assetsUnspendableAddr); - char tokensUnspendableAddr[64]; - GetTokensCCaddress(cpAssets, tokensUnspendableAddr, GetUnspendable(cpAssets, NULL)); - SetCCunspents(unspentOutputsAssets, tokensUnspendableAddr /*(char *)cpAssets->unspendableCCaddr*/); + char assetsTokensUnspendableAddr[64]; + std::vector vopretNonfungible; + GetNonfungibleData(refassetid, vopretNonfungible); + if (vopretNonfungible.size() > 0) + cpAssets->additionalTokensEvalcode2 = vopretNonfungible.begin()[0]; + GetTokensCCaddress(cpAssets, assetsTokensUnspendableAddr, GetUnspendable(cpAssets, NULL)); + SetCCunspents(unspentOutputsTokens, assetsTokensUnspendableAddr); - for (std::vector >::const_iterator itTokens = unspentOutputsTokens.begin(); + // tokenasks: + for (std::vector >::const_iterator itCoins = unspentOutputsCoins.begin(); + itCoins != unspentOutputsCoins.end(); + itCoins++) + addOrders(cpAssets, itCoins); + + // tokenbids: + for (std::vector >::const_iterator itTokens = unspentOutputsTokens.begin(); itTokens != unspentOutputsTokens.end(); itTokens++) - addOrders(cpTokens, itTokens); + addOrders(cpAssets, itTokens); - for (std::vector >::const_iterator itAssets = unspentOutputsAssets.begin(); - itAssets != unspentOutputsAssets.end(); - itAssets++) - addOrders(cpAssets, itAssets); - return(result); } @@ -328,14 +332,14 @@ std::string CreateBuyOffer(int64_t txfee, int64_t bidamount, uint256 assetid, in return (""); } - CPubKey unspendablePubkey = GetUnspendable(cpAssets, 0); - mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, bidamount, unspendablePubkey)); + CPubKey unspendableAssetsPubkey = GetUnspendable(cpAssets, 0); + mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, bidamount, unspendableAssetsPubkey)); mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, txfee, mypk)); std::vector voutTokenPubkeys; // should be empty - no token vouts return(FinalizeCCTx(0, cpAssets, mtx, mypk, txfee, - EncodeTokenOpRet(assetid, voutTokenPubkeys, - EncodeAssetOpRet('b', zeroid, pricetotal, Mypubkey())))); + EncodeTokenOpRet(assetid, voutTokenPubkeys, // TODO: actually this tx is not 'tokens', maybe it is better not to have token opret here but only asset opret. + EncodeAssetOpRet('b', zeroid, pricetotal, Mypubkey())))); // But still such token opret should not make problems because no token eval in these vouts } CCerror = strprintf("no coins found to make buy offer"); return(""); @@ -348,7 +352,6 @@ std::string CreateSell(int64_t txfee,int64_t askamount,uint256 assetid,int64_t p CPubKey mypk; uint64_t mask; int64_t inputs, CCchange; - CScript opret; struct CCcontract_info *cpAssets, assetsC; struct CCcontract_info *cpTokens, tokensC; @@ -359,7 +362,8 @@ std::string CreateSell(int64_t txfee,int64_t askamount,uint256 assetid,int64_t p return(""); } - cpAssets = CCinit(&assetsC, EVAL_ASSETS); // NOTE: this is for signing + cpAssets = CCinit(&assetsC, EVAL_ASSETS); // NOTE: for signing + if (txfee == 0) txfee = 10000; @@ -367,10 +371,11 @@ std::string CreateSell(int64_t txfee,int64_t askamount,uint256 assetid,int64_t p mypk = pubkey2pk(Mypubkey()); if (AddNormalinputs(mtx, mypk, 2*txfee, 3) > 0) { + std::vector vopretNonfungible; mask = ~((1LL << mtx.vin.size()) - 1); - // add single-eval tokens: - cpTokens = CCinit(&tokensC, EVAL_TOKENS); // NOTE: tokens is here - if ((inputs = AddTokenCCInputs(cpTokens, mtx, mypk, assetid, askamount, 60)) > 0) + // add single-eval tokens (or non-fungible tokens): + cpTokens = CCinit(&tokensC, EVAL_TOKENS); // NOTE: adding inputs only from EVAL_TOKENS cc + if ((inputs = AddTokenCCInputs(cpTokens, mtx, mypk, assetid, askamount, 60, vopretNonfungible)) > 0) { if (inputs < askamount) { //was: askamount = inputs; @@ -379,20 +384,26 @@ std::string CreateSell(int64_t txfee,int64_t askamount,uint256 assetid,int64_t p return (""); } - CPubKey unspendablePubkey = GetUnspendable(cpAssets, NULL); - mtx.vout.push_back(MakeTokensCC1vout(EVAL_ASSETS, askamount, unspendablePubkey)); - mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, txfee, mypk)); //marker + // if this is non-fungible tokens: + if( !vopretNonfungible.empty() ) + // set its evalcode + cpAssets->additionalTokensEvalcode2 = vopretNonfungible.begin()[0]; + + CPubKey unspendableAssetsPubkey = GetUnspendable(cpAssets, NULL); + mtx.vout.push_back(MakeTokensCC1vout(EVAL_ASSETS, cpAssets->additionalTokensEvalcode2, askamount, unspendableAssetsPubkey)); + mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, txfee, mypk)); //marker (seems, it is not for tokenorders) if (inputs > askamount) CCchange = (inputs - askamount); if (CCchange != 0) - mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, CCchange, mypk)); // change to single-eval token vout + // change to single-eval or non-fungible token vout (although for non-fungible token change currently is not possible) + mtx.vout.push_back(MakeTokensCC1vout((cpAssets->additionalTokensEvalcode2) ? cpAssets->additionalTokensEvalcode2 : EVAL_TOKENS, CCchange, mypk)); std::vector voutTokenPubkeys; - voutTokenPubkeys.push_back(unspendablePubkey); + voutTokenPubkeys.push_back(unspendableAssetsPubkey); - opret = EncodeTokenOpRet(assetid, voutTokenPubkeys, - EncodeAssetOpRet('s', zeroid, pricetotal, Mypubkey())); - return(FinalizeCCTx(mask,cpAssets, mtx, mypk, txfee, opret)); + return FinalizeCCTx(mask, cpTokens, mtx, mypk, txfee, + EncodeTokenOpRet(assetid, voutTokenPubkeys, vopretNonfungible, + EncodeAssetOpRet('s', zeroid, pricetotal, Mypubkey()))); } else { fprintf(stderr, "need some tokens to place ask\n"); @@ -430,7 +441,7 @@ std::string CreateSwap(int64_t txfee,int64_t askamount,uint256 assetid,uint256 a if (AddNormalinputs(mtx, mypk, txfee, 3) > 0) { mask = ~((1LL << mtx.vin.size()) - 1); - if ((inputs = AddAssetInputs(cp, mtx, mypk, assetid, askamount, 60)) > 0) + /*if ((inputs = AddAssetInputs(cp, mtx, mypk, assetid, askamount, 60)) > 0) { ////////////////////////// NOT IMPLEMENTED YET///////////////////////////////// if (inputs < askamount) { @@ -458,13 +469,13 @@ std::string CreateSwap(int64_t txfee,int64_t askamount,uint256 assetid,uint256 a else { opret = EncodeTokenOpRet(assetid, voutTokenPubkeys, EncodeAssetOpRet('e', assetid2, pricetotal, Mypubkey())); - } + } ////////////////////////// NOT IMPLEMENTED YET///////////////////////////////// return(FinalizeCCTx(mask,cp,mtx,mypk,txfee,opret)); } else { fprintf(stderr, "need some assets to place ask\n"); - } + } */ } else { // dimxy added 'else', because it was misleading message before fprintf(stderr,"need some native coins to place ask\n"); @@ -494,12 +505,13 @@ std::string CancelBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid) mask = ~((1LL << mtx.vin.size()) - 1); if (GetTransaction(bidtxid, vintx, hashBlock, false) != 0) { + std::vector 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))!=0) + if((funcid=DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, dummyEvalCode, dummyAssetid, dummyAssetid2, dummyPrice, dummyOrigpubkey, vopretNonfungible))!=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' } @@ -510,7 +522,7 @@ 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, + EncodeTokenOpRet(assetid, voutTokenPubkeys, vopretNonfungible, EncodeAssetOpRet('o', zeroid, 0, Mypubkey())))); } } @@ -523,8 +535,12 @@ std::string CancelSell(int64_t txfee,uint256 assetid,uint256 asktxid) CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); CTransaction vintx; uint64_t mask; uint256 hashBlock; int64_t askamount; - CPubKey mypk; struct CCcontract_info *cpTokens, *cpAssets, tokensC, assetsC; - uint8_t funcid,dummyEvalCode; uint256 dummyAssetid, dummyAssetid2; int64_t dummyPrice; std::vector dummyOrigpubkey; + CPubKey mypk; + struct CCcontract_info *cpTokens, *cpAssets, tokensC, assetsC; + uint8_t funcid, dummyEvalCode; + uint256 dummyAssetid, dummyAssetid2; + int64_t dummyPrice; + std::vector dummyOrigpubkey; cpAssets = CCinit(&assetsC, EVAL_ASSETS); @@ -538,27 +554,27 @@ std::string CancelSell(int64_t txfee,uint256 assetid,uint256 asktxid) mask = ~((1LL << mtx.vin.size()) - 1); if (GetTransaction(asktxid, vintx, hashBlock, false) != 0) { + std::vector 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))!=0) + if ((funcid=DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, dummyEvalCode, dummyAssetid, dummyAssetid2, dummyPrice, dummyOrigpubkey, vopretNonfungible))!=0) { - if (funcid == 's') mtx.vin.push_back(CTxIn(asktxid, 1, CScript())); // marker if funcid='s' - else if (funcid=='S') mtx.vin.push_back(CTxIn(asktxid, 3, CScript())); // marker if funcid='S' + if (funcid == 's') + mtx.vin.push_back(CTxIn(asktxid, 1, CScript())); // marker if funcid='s' + else if (funcid=='S') + mtx.vin.push_back(CTxIn(asktxid, 3, CScript())); // marker if funcid='S' } - mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, askamount, mypk)); // one-eval token vout + if (vopretNonfungible.size() > 0) + cpAssets->additionalTokensEvalcode2 = vopretNonfungible.begin()[0]; + + mtx.vout.push_back(MakeTokensCC1vout(cpAssets->additionalTokensEvalcode2 == 0 ? EVAL_TOKENS : cpAssets->additionalTokensEvalcode2, askamount, mypk)); // one-eval token vout mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); std::vector voutTokenPubkeys; voutTokenPubkeys.push_back(mypk); - /* char myCCaddr[65]; - uint8_t myPrivkey[32]; - Myprivkey(myPrivkey); - cpAssets = CCinit(&assetsC, EVAL_ASSETS); - GetCCaddress(cpAssets, myCCaddr, mypk); */ - // this is only for unspendable addresses: //CCaddr2set(cpTokens, EVAL_ASSETS, mypk, myPrivkey, myCCaddr); //do we need this? Seems FinalizeCCTx can attach to any evalcode cc addr by calling Getscriptaddress @@ -572,7 +588,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, + EncodeTokenOpRet(assetid, voutTokenPubkeys, vopretNonfungible, EncodeAssetOpRet('x', zeroid, 0, Mypubkey())))); } } @@ -614,8 +630,9 @@ std::string FillBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid,int64_t f SetAssetOrigpubkey(origpubkey, origprice, vintx); mtx.vin.push_back(CTxIn(bidtxid, bidvout, CScript())); // Coins on Assets unspendable - - if ((inputs = AddTokenCCInputs(cpTokens, mtx, mypk, assetid, fillamount, 60)) > 0) + + std::vector vopretNonfungible; + if ((inputs = AddTokenCCInputs(cpTokens, mtx, mypk, assetid, fillamount, 60, vopretNonfungible)) > 0) { if (inputs < fillamount) { std::cerr << "FillBuyOffer(): insufficient tokens to fill buy offer" << std::endl; @@ -624,6 +641,10 @@ std::string FillBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid,int64_t f } SetBidFillamounts(paid_amount, remaining_required, bidamount, fillamount, origprice); + + uint8_t additionalTokensEvalcode2 = 0; + if (vopretNonfungible.size() > 0) + additionalTokensEvalcode2 = vopretNonfungible.begin()[0]; if (inputs > fillamount) CCchange = (inputs - fillamount); @@ -634,13 +655,13 @@ std::string FillBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid,int64_t f mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, bidamount - paid_amount, unspendableAssetsPk)); // vout0 coins remainder mtx.vout.push_back(CTxOut(paid_amount,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); // vout1 coins to normal - mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, fillamount, pubkey2pk(origpubkey))); // vout2 single-eval tokens sent to the buyer - mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,txfee,origpubkey)); // vout3 marker to origpubkey + mtx.vout.push_back(MakeTokensCC1vout(additionalTokensEvalcode2 == 0 ? EVAL_TOKENS : additionalTokensEvalcode2, fillamount, pubkey2pk(origpubkey))); // vout2 single-eval tokens sent to the originator + mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, txfee, origpubkey)); // vout3 marker to origpubkey if (CCchange != 0) mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, CCchange, mypk)); // vout4 change in single-eval tokens - fprintf(stderr,"FillBuyOffer remaining %llu -> origpubkey\n", (long long)remaining_required); + fprintf(stderr,"FillBuyOffer() remaining %llu -> origpubkey\n", (long long)remaining_required); char unspendableAssetsAddr[64]; cpAssets = CCinit(&assetsC, EVAL_ASSETS); @@ -653,8 +674,8 @@ std::string FillBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid,int64_t f std::vector voutTokenPubkeys; voutTokenPubkeys.push_back(pubkey2pk(origpubkey)); - return(FinalizeCCTx(mask,cpTokens,mtx,mypk,txfee, - EncodeTokenOpRet(assetid, voutTokenPubkeys, + return(FinalizeCCTx(mask, cpTokens, mtx, mypk, txfee, + EncodeTokenOpRet(assetid, voutTokenPubkeys, vopretNonfungible, EncodeAssetOpRet('B', zeroid, remaining_required, origpubkey)))); } else return("dont have any assets to fill bid"); } @@ -691,15 +712,21 @@ std::string FillSell(int64_t txfee, uint256 assetid, uint256 assetid2, uint256 a return(""); } + std::vector vopretNonfungible; + uint8_t additionalTokensEvalcode2 = 0; + GetNonfungibleData(assetid, vopretNonfungible); + if (vopretNonfungible.size() > 0) + additionalTokensEvalcode2 = vopretNonfungible.begin()[0]; + cpAssets = CCinit(&assetsC, EVAL_ASSETS); if (txfee == 0) txfee = 10000; mypk = pubkey2pk(Mypubkey()); - if (AddNormalinputs(mtx, mypk, 2*txfee, 3) > 0) - { - mask = ~((1LL << mtx.vin.size()) - 1); + //if (AddNormalinputs(mtx, mypk, 2*txfee, 3) > 0) + //{ + //mask = ~((1LL << mtx.vin.size()) - 1); if (GetTransaction(asktxid, vintx, hashBlock, false) != 0) { orig_assetoshis = vintx.vout[askvout].nValue; @@ -707,13 +734,12 @@ std::string FillSell(int64_t txfee, uint256 assetid, uint256 assetid2, uint256 a dprice = (double)total_nValue / orig_assetoshis; paid_nValue = dprice * fillunits; - mtx.vin.push_back(CTxIn(asktxid, askvout, CScript())); // NOTE: this is the reference to tokens -> send cpTokens for signing into FinalizeCCTx! - - if (assetid2 != zeroid) - inputs = AddAssetInputs(cpAssets, mtx, mypk, assetid2, paid_nValue, 60); // not implemented yet + if (assetid2 != zeroid) { + inputs = 0; // = AddAssetInputs(cpAssets, mtx, mypk, assetid2, paid_nValue, 60); // not implemented yet + } else { - inputs = AddNormalinputs(mtx, mypk, paid_nValue, 60); + inputs = AddNormalinputs(mtx, mypk, 2 * txfee + paid_nValue, 60); // Better to use single AddNormalinputs() to allow payment if user has only single utxo with normal funds mask = ~((1LL << mtx.vin.size()) - 1); } if (inputs > 0) @@ -723,7 +749,10 @@ std::string FillSell(int64_t txfee, uint256 assetid, uint256 assetid2, uint256 a CCerror = strprintf("insufficient coins to fill sell"); return (""); } - + + // cc vin should be after normal vin + mtx.vin.push_back(CTxIn(asktxid, askvout, CScript())); + if (assetid2 != zeroid) SetSwapFillamounts(received_assetoshis, remaining_nValue, orig_assetoshis, paid_nValue, total_nValue); //not implemented correctly yet else @@ -732,11 +761,11 @@ std::string FillSell(int64_t txfee, uint256 assetid, uint256 assetid2, uint256 a if (assetid2 != zeroid && inputs > paid_nValue) CCchange = (inputs - paid_nValue); - mtx.vout.push_back(MakeTokensCC1vout(EVAL_ASSETS, orig_assetoshis - received_assetoshis, GetUnspendable(cpAssets, NULL))); // vout.0 tokens remainder to unspendable cc addr - mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, received_assetoshis, mypk)); //vout.1 purchased tokens to self single-eval addr + // vout.0 tokens remainder to unspendable cc addr: + mtx.vout.push_back(MakeTokensCC1vout(EVAL_ASSETS, additionalTokensEvalcode2, orig_assetoshis - received_assetoshis, GetUnspendable(cpAssets, NULL))); + //vout.1 purchased tokens to self token single-eval or dual-eval token+nonfungible cc addr: + mtx.vout.push_back(MakeTokensCC1vout(additionalTokensEvalcode2 == 0 ? EVAL_TOKENS : additionalTokensEvalcode2, received_assetoshis, mypk)); - // NOTE: no marker here - if (assetid2 != zeroid) { std::cerr << "FillSell() WARNING: asset swap not implemented yet! (paid_nValue)" << std::endl; // TODO: change MakeCC1vout appropriately when implementing: @@ -768,14 +797,16 @@ std::string FillSell(int64_t txfee, uint256 assetid, uint256 assetid2, uint256 a std::vector voutTokenPubkeys; voutTokenPubkeys.push_back(mypk); + cpAssets->additionalTokensEvalcode2 = additionalTokensEvalcode2; + return(FinalizeCCTx(mask, cpAssets, mtx, mypk, txfee, - EncodeTokenOpRet(assetid, voutTokenPubkeys, + EncodeTokenOpRet(assetid, voutTokenPubkeys, vopretNonfungible, EncodeAssetOpRet(assetid2 != zeroid ? 'E' : 'S', assetid2, remaining_nValue, origpubkey)))); } else { CCerror = strprintf("filltx not enough utxos"); fprintf(stderr,"%s\n", CCerror.c_str()); } } - } + //} return(""); } diff --git a/src/cc/CCcustom.cpp b/src/cc/CCcustom.cpp index 8e878d37d..d2cb811cd 100644 --- a/src/cc/CCcustom.cpp +++ b/src/cc/CCcustom.cpp @@ -290,6 +290,9 @@ int32_t CClib_initcp(struct CCcontract_info *cp,uint8_t evalcode) struct CCcontract_info *CCinit(struct CCcontract_info *cp, uint8_t evalcode) { + // important to clear because not all members are always initialized! + memset(cp, '\0', sizeof(*cp)); + cp->evalcode = evalcode; switch ( evalcode ) { diff --git a/src/cc/CCinclude.h b/src/cc/CCinclude.h index 4da696547..1cb141a16 100644 --- a/src/cc/CCinclude.h +++ b/src/cc/CCinclude.h @@ -89,6 +89,7 @@ struct CCcontract_info { // this is for spending from 'unspendable' CC address uint8_t evalcode; + uint8_t additionalTokensEvalcode2; // this is for making three-eval-token vouts (EVAL_TOKENS + evalcode + additionalEvalcode2) char unspendableCCaddr[64], CChexstr[72], normaladdr[64]; uint8_t CCpriv[32]; @@ -103,7 +104,7 @@ struct CCcontract_info // this is for spending from two additional 'unspendable' CC addresses of other eval codes // (that is, for spending from several cc contract 'unspendable' addresses): - uint8_t evalcode2, evalcode3; + uint8_t unspendableEvalcode2, unspendableEvalcode3; // changed evalcodeN to unspendableEvalcodeN for not mixing up with additionalEvalcodeN char unspendableaddr2[64], unspendableaddr3[64]; uint8_t unspendablepriv2[32], unspendablepriv3[32]; CPubKey unspendablepk2, unspendablepk3; @@ -160,17 +161,19 @@ uint256 OraclesBatontxid(uint256 oracletxid,CPubKey pk); //int64_t AddAssetInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint256 assetid,int64_t total,int32_t maxinputs); int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs); +int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs, std::vector &vopretNonfungible); int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, int32_t v, uint256 reftokenid); bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx); -CScript EncodeAssetOpRet(uint8_t assetFuncId, uint256 assetid2, int64_t price, std::vector origpubkey); -//bool DecodeAssetCreateOpRet(const CScript &scriptPubKey, std::vector &origpubkey, std::string &name, std::string &description); -uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &evalCodeInOpret, uint256 &tokenid, uint256 &assetid2, int64_t &price, std::vector &origpubkey); +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(uint8_t tokenFuncId, uint8_t evalCodeInOpret, 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 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); @@ -193,12 +196,14 @@ CC* GetCryptoCondition(CScript const& scriptSig); void CCaddr2set(struct CCcontract_info *cp,uint8_t evalcode,CPubKey pk,uint8_t *priv,char *coinaddr); void CCaddr3set(struct CCcontract_info *cp,uint8_t evalcode,CPubKey pk,uint8_t *priv,char *coinaddr); void CCaddr1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, char *coinaddr); - CTxOut MakeTokensCC1of2vout(uint8_t evalcode, CAmount nValue, CPubKey pk1, CPubKey pk2); +CTxOut MakeTokensCC1of2vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk1, CPubKey pk2); CTxOut MakeTokensCC1vout(uint8_t evalcode, CAmount nValue, CPubKey pk); +CTxOut MakeTokensCC1vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk); +CC *MakeTokensCCcond1of2(uint8_t evalcode, uint8_t evalcode2, CPubKey pk1, CPubKey pk2); CC *MakeTokensCCcond1of2(uint8_t evalcode, CPubKey pk1, CPubKey pk2); CC *MakeTokensCCcond1(uint8_t evalcode, CPubKey pk); - +CC *MakeTokensCCcond1(uint8_t evalcode, uint8_t evalcode2, 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); @@ -245,4 +250,29 @@ void vcalc_sha256(char deprecated[(256 >> 3) * 2 + 1],uint8_t hash[256 >> 3],uin bits256 bits256_doublesha256(char *deprecated,uint8_t *data,int32_t datalen); UniValue ValueFromAmount(const CAmount& amount); + +// bitcoin LogPrintStr with category "-debug" cmdarg support for C++ ostringstream: +#define CCLOG_INFO 0 +#define CCLOG_DEBUG1 1 +#define CCLOG_DEBUG2 2 +#define CCLOG_DEBUG3 3 +template +inline void CCLogPrintStream(char *category, int level, T print_to_stream) +{ + std::ostringstream stream; + print_to_stream(stream); + if (level < 0) + level = 0; + if (level > 3) + level = 3; + for (int i = 0; i < level; i++) + if (LogAcceptCategory((std::string(category) + (level > 0 ? std::string("-") + std::to_string(level) : std::string(""))).c_str())) { + LogPrintStr(stream.str()); + break; + } +} + +#define LOGSTREAM(category, level, logoperator) CCLogPrintStream( category, level, [=](std::ostringstream &stream) {logoperator;} ) + + #endif diff --git a/src/cc/CCtokens.cpp b/src/cc/CCtokens.cpp index d7db32b99..23a4fda50 100644 --- a/src/cc/CCtokens.cpp +++ b/src/cc/CCtokens.cpp @@ -43,15 +43,25 @@ // NOTE: this inital tx won't be used by other contract // for tokens to be used there should be at least one 't' tx with other contract's custom opret -CScript EncodeTokenCreateOpRet(uint8_t funcid,std::vector origpubkey,std::string name,std::string description) +CScript EncodeTokenCreateOpRet(uint8_t funcid, std::vector origpubkey, std::string name, std::string description, std::vector vopretNonfungible ) { - CScript opret; uint8_t evalcode = EVAL_TOKENS; - opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << origpubkey << name << description); + CScript opret; + uint8_t evalcode = EVAL_TOKENS; + funcid = 'c'; // override the param + + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << origpubkey << name << description; \ + if(!vopretNonfungible.empty()) 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 opret; uint8_t tokenFuncId = 't'; @@ -60,17 +70,20 @@ CScript EncodeTokenOpRet(uint256 tokenid, std::vector voutPubkeys, CScr tokenid = revuint256(tokenid); uint8_t ccType = 0; - if (voutPubkeys.size() >= 1 && voutPubkeys.size() <= 2) + if (voutPubkeys.size() >= 0 && voutPubkeys.size() <= 2) ccType = voutPubkeys.size(); + else { + LOGSTREAM("tokens", CCLOG_INFO, stream << "EncodeTokenOpRet voutPubkeys.size()=" << voutPubkeys.size() << " not supported" << std::endl); + } std::vector vpayload; GetOpReturnData(payload, vpayload); - opret << OP_RETURN << E_MARSHAL(ss << evalCodeInOpret << tokenFuncId << tokenid << ccType; \ - if (ccType >= 1) ss << voutPubkeys[0]; \ - if (ccType == 2) ss << voutPubkeys[1]; \ - if (vpayload.size() > 0) ss << vpayload;); - + 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": // if (payload.size() > 0) @@ -96,21 +109,50 @@ CScript EncodeTokenOpRet(uint8_t tokenFuncId, uint8_t evalCodeInOpret, uint256 t return EncodeTokenOpRet(tokenid, voutPubkeys, payload); } -uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey,std::vector &origpubkey,std::string &name,std::string &description) +// overload for fungible: +uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey, std::vector &origpubkey, std::string &name, std::string &description) { + std::vector vopretNonfungibleDummy; + return DecodeTokenCreateOpRet(scriptPubKey, origpubkey, name, description, vopretNonfungibleDummy); +} + +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; 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) != 0 ) + 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 ) ) return(funcid); } return (uint8_t)0; } -uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCode, uint256 &tokenid, std::vector &voutPubkeys, std::vector &vopretExtra) +// 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) { + std::vector vopret1, vopret2; + uint8_t funcId = DecodeTokenOpRet(scriptPubKey, evalCode, tokenid, voutPubkeys, vopret1, vopret2); + + CScript opretExtra; + vopretExtra.clear(); + + // make marshalling for compatibility + // callers of this func expect length of full array at the beginning (and they will make 'vopretStripped' from vopretExtra) + if (vopret2.empty()) + opretExtra << OP_RETURN << E_MARSHAL(ss << vopret1); // if first opret (or no oprets) + else + opretExtra << OP_RETURN << E_MARSHAL(ss << vopret2); // if both oprets present, return assets/heir/gateways/... opret (dump non-fungible opret) + + 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) { std::vector vopret, extra, dummyPubkey; uint8_t funcId=0, *script, dummyEvalCode, dummyFuncId, ccType; @@ -121,32 +163,39 @@ uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCode, uint256 script = (uint8_t *)vopret.data(); tokenid = zeroid; - if (script != 0 && vopret.size() > 2) + if (script != NULL && vopret.size() > 2) { // 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 isEof = true; - evalCode = script[0]; - if (evalCode != EVAL_TOKENS) + evalCodeTokens = script[0]; + if (evalCodeTokens != EVAL_TOKENS) return (uint8_t)0; funcId = script[1]; - //fprintf(stderr,"decode.[%c]\n",funcId); + LOGSTREAM("tokens", CCLOG_DEBUG1, stream << "DecodeTokenOpRet decoded funcId=" << (char)(funcId?funcId:' ')); switch( funcId ) { case 'c': - return DecodeTokenCreateOpRet(scriptPubKey, dummyPubkey, dummyName, dummyDescription); + return DecodeTokenCreateOpRet(scriptPubKey, dummyPubkey, dummyName, dummyDescription, vopret1); //break; case 't': //not used yet: case 'l': // NOTE: 'E_UNMARSHAL result==false' means 'parse error' OR 'not eof state'. Consequently, 'result==false' but 'isEof==true' means just 'parse error' - if (E_UNMARSHAL(vopret, ss >> dummyEvalCode; ss >> dummyFuncId; ss >> tokenid; ss >> ccType; if (ccType >= 1) ss >> voutPubkey1; if (ccType == 2) ss >> voutPubkey2; isEof = ss.eof(); vopretExtra = std::vector(ss.begin(), ss.end())) - || !isEof) + if (E_UNMARSHAL(vopret, ss >> dummyEvalCode; ss >> dummyFuncId; ss >> tokenid; ss >> ccType; \ + 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 something else remains -> bad format + isEof = ss.eof()) || !isEof) { if (!(ccType >= 0 && ccType <= 2)) { //incorrect ccType - std::cerr << "DecodeTokenOpRet() incorrect ccType=" << (int)ccType << " tokenid=" << revuint256(tokenid).GetHex() << std::endl; + LOGSTREAM("tokens", CCLOG_INFO, stream << "DecodeTokenOpRet() incorrect ccType=" << (int)ccType << " tokenid=" << revuint256(tokenid).GetHex() << std::endl); return (uint8_t)0; } @@ -160,22 +209,20 @@ uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCode, uint256 tokenid = revuint256(tokenid); return(funcId); } - std::cerr << "DecodeTokenOpRet() bad opret format, isEof=" << isEof << " ccType=" << ccType << " tokenid=" << revuint256(tokenid).GetHex() << std::endl; + LOGSTREAM("tokens", CCLOG_INFO, stream << "DecodeTokenOpRet() bad opret format, isEof=" << isEof << " ccType=" << ccType << " tokenid=" << revuint256(tokenid).GetHex() << std::endl); return (uint8_t)0; default: - std::cerr << "DecodeTokenOpRet() illegal funcid=" << (int)funcId << std::endl; + LOGSTREAM("tokens", CCLOG_INFO, stream << "DecodeTokenOpRet() illegal funcid=" << (int)funcId << std::endl); return (uint8_t)0; } } else { - std::cerr << "DecodeTokenOpRet() empty opret, could not parse" << std::endl; + LOGSTREAM("tokens", CCLOG_INFO, stream << "DecodeTokenOpRet() empty opret, could not parse" << std::endl); } return (uint8_t)0; } - - // tx validation bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn) { @@ -183,7 +230,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 vopretExtra, tmporigpubkey, ignorepubkey; + std::vector vopret1, vopret2, tmporigpubkey, ignorepubkey; uint8_t funcid, evalCodeInOpret; char destaddr[64], origaddr[64], CCaddr[64]; std::vector voutTokenPubkeys; @@ -195,10 +242,10 @@ 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, vopretExtra)) == 0) + if ((funcid = DecodeTokenOpRet(tx.vout[numvouts - 1].scriptPubKey, evalCodeInOpret, tokenid, voutTokenPubkeys, vopret1, vopret2)) == 0) return eval->Invalid("TokenValidate: invalid opreturn payload"); - fprintf(stderr, "TokensValidate (%c) evalcode=0x%0x\n", funcid, cp->evalcode); + LOGSTREAM("tokens", CCLOG_INFO, stream << "TokensValidate funcId=" << (char)(funcid?funcid:' ') << " evalcode=" << cp->evalcode); if (eval->GetTxUnconfirmed(tokenid, createTx, hashBlock) == 0) return eval->Invalid("cant find token create txid"); @@ -218,6 +265,12 @@ 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) { @@ -240,11 +293,11 @@ bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction & if (inputs == 0) return eval->Invalid("no token inputs for transfer"); - fprintf(stderr, "token transfer preliminarily validated %.8f -> %.8f (%d %d)\n", (double)inputs / COIN, (double)outputs / COIN, preventCCvins, preventCCvouts); + LOGSTREAM("tokens", CCLOG_INFO, stream << "token transfer preliminarily validated inputs=" << inputs << "->outputs=" << outputs << " preventCCvins=" << preventCCvins<< " preventCCvouts=" << preventCCvouts << std::endl); break; // breaking to other contract validation... default: - fprintf(stderr, "illegal tokens funcid.(%c)\n", funcid); + LOGSTREAM("tokens", CCLOG_INFO, stream << "illegal tokens funcid=" << (char)(funcid?funcid:' ') << std::endl); return eval->Invalid("unexpected token funcid"); } @@ -266,7 +319,7 @@ bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction & // helper funcs: -// extract my vins pubkeys: +// extract cc token vins' pubkeys: bool ExtractTokensVinPubkeys(CTransaction tx, std::vector &vinPubkeys) { bool found = false; @@ -276,12 +329,13 @@ bool ExtractTokensVinPubkeys(CTransaction tx, std::vector &vinPubkeys) cpTokens = CCinit(&tokensC, EVAL_TOKENS); for (int32_t i = 0; i < tx.vin.size(); i++) - { // check for additional contracts which may send tokens to the Tokens contract + { + // check for cc token vins: if( (*cpTokens->ismyvin)(tx.vin[i].scriptSig) ) { auto findEval = [](CC *cond, struct CCVisitor _) { - bool r = false; //cc_typeId(cond) == CC_Eval && cond->codeLength == 1 && cond->code[0] == EVAL_TOKENS; + bool r = false; if (cc_typeId(cond) == CC_Secp256k1) { *(CPubKey*)_.context = buf2pk(cond->publicKey); @@ -313,38 +367,49 @@ bool ExtractTokensVinPubkeys(CTransaction tx, std::vector &vinPubkeys) thread_local uint32_t tokenValIndentSize = 0; // validates opret for token tx: -uint8_t ValidateTokenOpret(CTransaction tx, int32_t v, uint256 tokenid, std::vector &voutPubkeys, std::vector &vopretExtra) { +uint8_t ValidateTokenOpret(CTransaction tx, uint256 tokenid) { - uint256 tokenidOpret, tokenidOpret2; + uint256 tokenidOpret = zeroid; uint8_t funcid; uint8_t dummyEvalCode; + std::vector voutPubkeysDummy; + std::vector vopretExtraDummy; // 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 (tx.vout.size() == 0) + return (uint8_t)0; - if ((funcid = DecodeTokenOpRet(tx.vout[n - 1].scriptPubKey, dummyEvalCode, tokenidOpret, voutPubkeys, vopretExtra)) == 0) + if ((funcid = DecodeTokenOpRet(tx.vout.back().scriptPubKey, dummyEvalCode, tokenidOpret, voutPubkeysDummy, vopretExtraDummy)) == 0) { - std::cerr << indentStr << "ValidateTokenOpret() DecodeTokenOpret could not parse opret for txid=" << tx.GetHash().GetHex() << std::endl; - return(false); + LOGSTREAM("tokens", CCLOG_INFO, stream << indentStr << "ValidateTokenOpret() DecodeTokenOpret could not parse opret for txid=" << tx.GetHash().GetHex() << std::endl); + return (uint8_t)0; } else if (funcid == 'c') { - if (tokenid != zeroid && tokenid == tx.GetHash() && v == 0) { - //std::cerr << indentStr << "ValidateTokenOpret() this is the tokenbase 'c' tx, txid=" << tx.GetHash().GetHex() << " vout=" << v << " returning true" << std::endl; + if (tokenid != zeroid && tokenid == tx.GetHash()) { + LOGSTREAM("tokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() this is the tokenbase 'c' tx, txid=" << tx.GetHash().GetHex() << " returning true" << std::endl); return funcid; } + else { + LOGSTREAM("tokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() not my tokenbase txid=" << tx.GetHash().GetHex() << std::endl); + } } else if (funcid == 't') { //std::cerr << indentStr << "ValidateTokenOpret() tokenid=" << tokenid.GetHex() << " tokenIdOpret=" << tokenidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl; if (tokenid != zeroid && tokenid == tokenidOpret) { - //std::cerr << indentStr << "ValidateTokenOpret() this is a transfer 't' tx, txid=" << tx.GetHash().GetHex() << " vout=" << v << " returning true" << std::endl; + LOGSTREAM("tokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() this is a transfer 't' tx, txid=" << tx.GetHash().GetHex() << " returning true" << std::endl); return funcid; } + else { + LOGSTREAM("tokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() not my tokenid=" << tokenidOpret.GetHex() << std::endl); + } } - //std::cerr << indentStr << "ValidateTokenOpret() return false funcid=" << (char)funcid << " tokenid=" << tokenid.GetHex() << " tokenIdOpret=" << tokenidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl; + else { + LOGSTREAM("tokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() not supported funcid=" << (char)funcid << " tokenIdOpret=" << tokenidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl); + } return (uint8_t)0; } @@ -358,20 +423,20 @@ int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *c // this is just for log messages indentation fur debugging recursive calls: std::string indentStr = std::string().append(tokenValIndentSize, '.'); - //std::cerr << indentStr << "IsTokensvout() entered for txid=" << tx.GetHash().GetHex() << " v=" << v << " for tokenid=" << reftokenid.GetHex() << std::endl; + + LOGSTREAM("tokens", CCLOG_DEBUG2, stream << indentStr << "IsTokensvout() entered for txid=" << tx.GetHash().GetHex() << " v=" << v << " for tokenid=" << reftokenid.GetHex() << std::endl); + + int32_t n = tx.vout.size(); + // just check boundaries: + if (n == 0 || v < 0 || v >= n-1) { + LOGSTREAM("tokens", CCLOG_INFO, stream << indentStr << "isTokensvout() incorrect params: (n == 0 or v < 0 or v >= n-1)" << " v=" << v << " n=" << n << " returning 0" << std::endl); + return(0); + } //TODO: validate cc vouts are EVAL_TOKENS! if (tx.vout[v].scriptPubKey.IsPayToCryptoCondition()) // maybe check address too? dimxy: possibly no, because there are too many cases with different addresses here { - int32_t n = tx.vout.size(); - // just check boundaries: - if (v >= n - 1) { // just moved this up (dimxy) - std::cerr << indentStr << "isTokensvout() internal err: (v >= n - 1), returning 0" << std::endl; - return(0); - } - if (goDeeper) { - //std::cerr << indentStr << "IsTokensvout() maxTokenExactAmountDepth=" << maxTokenExactAmountDepth << std::endl; //validate all tx int64_t myCCVinsAmount = 0, myCCVoutsAmount = 0; @@ -384,98 +449,109 @@ int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *c // if ccInputs != ccOutputs and it is not the tokenbase tx // this means it is possibly a fake tx (dimxy): if (reftokenid != tx.GetHash()) { // checking that this is the true tokenbase tx, by verifying that funcid=c, is done further in this function (dimxy) - std::cerr << indentStr << "IsTokensvout() warning: for the verified tx detected a bad vintx=" << tx.GetHash().GetHex() << ": cc inputs != cc outputs and not the 'tokenbase' tx, skipping the verified tx" << std::endl; + LOGSTREAM("tokens", CCLOG_INFO, stream << indentStr << "IsTokensvout() warning: for the verified tx detected a bad vintx=" << tx.GetHash().GetHex() << ": cc inputs != cc outputs and not the 'tokenbase' tx, skipping the verified tx" << std::endl); return 0; } } } - // moved opret checking to this new reusable func (dimxy): - std::vector voutPubkeys; - std::vector vopretExtra; - const uint8_t funcId = ValidateTokenOpret(tx, v, reftokenid, voutPubkeys, vopretExtra); + // 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) { - //std::cerr << indentStr << "IsTokensvout() ValidateTokenOpret returned not-null" << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; + LOGSTREAM("tokens", 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 (checkPubkeys && funcId != 'c') { // verify that the vout is token's (for 'c' there is no pubkeys!): + 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::cerr << "IsTokensvout() vopretExtra=" << HexStr(vopretExtra) << std::endl; - - uint8_t evalCodeInOpret; - if (vopretExtra.size() >= 2 /*|| vopretExtra.size() != vopretExtra.begin()[0] <-- shold we check this?*/) { - evalCodeInOpret = vopretExtra.begin()[1]; - } - else { - // if payload is empty maybe it is a claim to non-payload-one-token-eval vout? - evalCodeInOpret = EVAL_TOKENS; - } + 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("tokens", CCLOG_DEBUG2, stream << "IsTokensvout() vopret1=" << HexStr(vopret1) << std::endl); + LOGSTREAM("tokens", 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) { - CTxOut testDualVout; - // check dual-eval 1 pubkey vout with the first pubkey - testDualVout = MakeTokensCC1vout(evalCodeInOpret, tx.vout[v].nValue, voutPubkeys[0]); - if (tx.vout[v].scriptPubKey == testDualVout.scriptPubKey) { - //std::cerr << indentStr << "IsTokensvout() this is dual-eval token vout (i=0), eval2=" << (int)evalCodeInOpret << ", returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; - return tx.vout[v].nValue; - } - - if(voutPubkeys.size() == 2) { - // check dual eval 1of2 pubkeys vout - testDualVout = MakeTokensCC1of2vout(evalCodeInOpret, tx.vout[v].nValue, voutPubkeys[0], voutPubkeys[1]); - if (tx.vout[v].scriptPubKey == testDualVout.scriptPubKey) { - //std::cerr << indentStr << "IsTokensvout() this is dual-eval token 1of2 vout, eval2=" << (int)evalCodeInOpret << ", returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; - return tx.vout[v].nValue; - } + // check dual/three-eval 1 pubkey vout with the first pubkey + testVouts.push_back( std::make_pair(MakeTokensCC1vout(evalCode, evalCode2, tx.vout[v].nValue, voutPubkeys[0]), std::string("three-eval cc1 pk[0]")) ); + if (evalCode2 != 0) + // also check in backward evalcode order + testVouts.push_back( std::make_pair(MakeTokensCC1vout(evalCode2, evalCode, tx.vout[v].nValue, voutPubkeys[0]), std::string("three-eval cc1 pk[0] backward-eval")) ); - // check dual eval 1 pubkey vout with the second pubkey - testDualVout = MakeTokensCC1vout(evalCodeInOpret, tx.vout[v].nValue, voutPubkeys[1]); - if (tx.vout[v].scriptPubKey == testDualVout.scriptPubKey) { - //std::cerr << indentStr << "IsTokensvout() this is dual-eval token vout (i=1), eval2=" << (int)evalCodeInOpret << ", returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; - return tx.vout[v].nValue; - } + if(voutPubkeys.size() == 2) { + // check dual/three eval 1of2 pubkeys vout + testVouts.push_back( std::make_pair(MakeTokensCC1of2vout(evalCode, evalCode2, tx.vout[v].nValue, voutPubkeys[0], voutPubkeys[1]), std::string("three-eval cc1of2")) ); + // check dual/three eval 1 pubkey vout with the second pubkey + testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode, evalCode2, tx.vout[v].nValue, voutPubkeys[1]), std::string("three-eval cc1 pk[1]"))); + if (evalCode2 != 0) { + // also check in backward evalcode order: + // check dual/three eval 1of2 pubkeys vout + testVouts.push_back(std::make_pair(MakeTokensCC1of2vout(evalCode2, evalCode, tx.vout[v].nValue, voutPubkeys[0], voutPubkeys[1]), std::string("three-eval cc1of2 backward-eval"))); + // check dual/three eval 1 pubkey vout with the second pubkey + testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, evalCode, tx.vout[v].nValue, voutPubkeys[1]), std::string("three-eval cc1 pk[1] backward-eval"))); + } } - // maybe this is claim to single-eval token? - CTxOut testTokenVout1; - testTokenVout1 = MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, voutPubkeys[0]); - if (tx.vout[v].scriptPubKey == testTokenVout1.scriptPubKey) { - //std::cerr << indentStr << "IsTokensvout() this is single-eval token vout (i=0), returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; - return tx.vout[v].nValue; - } + // maybe this is like gatewayclaim to single-eval token? + testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, voutPubkeys[0]), std::string("single-eval cc1 pk[0]"))); + // maybe this is like FillSell for non-fungible token? + if( evalCode != 0 ) + testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode, tx.vout[v].nValue, voutPubkeys[0]), std::string("dual-eval-token cc1 pk[0]"))); + if( evalCode2 != 0 ) + testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, tx.vout[v].nValue, voutPubkeys[0]), std::string("dual-eval2-token cc1 pk[0]"))); if (voutPubkeys.size() == 2) { - testTokenVout1 = MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, voutPubkeys[1]); - if (tx.vout[v].scriptPubKey == testTokenVout1.scriptPubKey) { - //std::cerr << indentStr << "IsTokensvout() this is single-eval token vout (i=1), returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; - return tx.vout[v].nValue; - } + // the same for pk[1]: + testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, voutPubkeys[1]), std::string("single-eval cc1 pk[1]"))); + if (evalCode != 0) + testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode, tx.vout[v].nValue, voutPubkeys[1]), std::string("dual-eval-token cc1 pk[1]"))); + if (evalCode2 != 0) + testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, tx.vout[v].nValue, voutPubkeys[1]), std::string("dual-eval2-token cc1 pk[1]"))); } } - // maybe it is single-eval or dual-eval token change? + // maybe it is single-eval or dual/three-eval token change? std::vector vinPubkeys; ExtractTokensVinPubkeys(tx, vinPubkeys); - for(std::vector::iterator it = vinPubkeys.begin(); it != vinPubkeys.end(); it++) { - CTxOut testTokenVout1 = MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, *it); - CTxOut testDualVout1 = MakeTokensCC1vout(evalCodeInOpret, tx.vout[v].nValue, *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"))); - if (tx.vout[v].scriptPubKey == testTokenVout1.scriptPubKey) { - //std::cerr << indentStr << "IsTokensvout() this is single-eval token change, returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; - return tx.vout[v].nValue; - } - - if (tx.vout[v].scriptPubKey == testDualVout1.scriptPubKey) { - //std::cerr << indentStr << "IsTokensvout() this is dual-eval token change, vout eval2=" << (int)evalCodeInOpret << ", returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; - return tx.vout[v].nValue; - } + if (evalCode2 != 0) + // also check in backward evalcode order: + 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("tokens", 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("tokens", 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 { - //std::cerr << indentStr << "IsTokensvout() returns without pubkey check value=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; + LOGSTREAM("tokens", 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; } } @@ -487,7 +563,7 @@ int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *c } // compares cc inputs vs cc outputs (to prevent feeding vouts from normal inputs) -bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cp, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 tokenid) +bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cp, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 reftokenid) { CTransaction vinTx; uint256 hashBlock; @@ -503,6 +579,8 @@ bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cp, int64_t &inpu // this is just for log messages indentation for debugging recursive calls: std::string indentStr = std::string().append(tokenValIndentSize, '.'); + LOGSTREAM("tokens", CCLOG_DEBUG2, stream << indentStr << "TokensExactAmounts() entered for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl); + for (int32_t i = 0; iismyvin)(tx.vin[i].scriptSig) /*|| IsVinAllowed(tx.vin[i].scriptSig) != 0*/) @@ -511,37 +589,38 @@ bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cp, int64_t &inpu // we are not inside the validation code -- dimxy if ((eval && eval->GetTxUnconfirmed(tx.vin[i].prevout.hash, vinTx, hashBlock) == 0) || (!eval && !myGetTransaction(tx.vin[i].prevout.hash, vinTx, hashBlock))) { - std::cerr << indentStr << "TokensExactAmounts() cannot read vintx for i." << i << " numvins." << numvins << std::endl; + LOGSTREAM("tokens", CCLOG_INFO, stream << indentStr << "TokensExactAmounts() cannot read vintx for i." << i << " numvins." << numvins << std::endl); return (!eval) ? false : eval->Invalid("always should find vin tx, but didnt"); } else { - tokenValIndentSize++; - // validate vouts of vintx - //std::cerr << indentStr << "TokenExactAmounts() check vin i=" << i << " nValue=" << vinTx.vout[tx.vin[i].prevout.n].nValue << std::endl; - tokenoshis = IsTokensvout(goDeeper, true, cpTokens, eval, vinTx, tx.vin[i].prevout.n, tokenid); + LOGSTREAM("tokens", CCLOG_DEBUG2, stream << indentStr << "TokenExactAmounts() checking vintx.vout for tx.vin[" << i << "] nValue=" << vinTx.vout[tx.vin[i].prevout.n].nValue << std::endl); + + // validate vouts of vintx + tokenValIndentSize++; + tokenoshis = IsTokensvout(goDeeper, true, cpTokens, eval, vinTx, tx.vin[i].prevout.n, reftokenid); tokenValIndentSize--; if (tokenoshis != 0) { - std::cerr << indentStr << "TokensExactAmounts() vin i=" << i << " tokenoshis=" << tokenoshis << std::endl; + LOGSTREAM("tokens", CCLOG_DEBUG1, stream << indentStr << "TokensExactAmounts() adding vintx.vout for tx.vin[" << i << "] tokenoshis=" << tokenoshis << std::endl); inputs += tokenoshis; } } } } - - for (int32_t i = 0; iInvalid() here! } else return true; } -// add inputs from token cc addr -int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs) +// get non-fungible data from 'tokenbase' tx (the data might be empty) +void GetNonfungibleData(uint256 tokenid, std::vector &vopretNonfungible) +{ + CTransaction tokenbasetx; + uint256 hashBlock; + + if (!myGetTransaction(tokenid, tokenbasetx, hashBlock)) { + LOGSTREAM("tokens", CCLOG_INFO, stream << "SetNonfungibleEvalCode() 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 vopretExtra; + DecodeTokenOpRet(tokenbasetx.vout.back().scriptPubKey, dummyEvalCode, tokenIdOpret, voutPubkeys, vopretNonfungible, vopretExtra); + } +} + + +// overload, adds inputs from token cc addr +int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs) { + std::vector vopretNonfungibleDummy; + return AddTokenCCInputs(cp, mtx, pk, tokenid, total, maxinputs, vopretNonfungibleDummy); +} + +// adds inputs from token cc addr and returns non-fungible opret payload if present +// also sets evalcode in cp, if needed +int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs, std::vector &vopretNonfungible) { char tokenaddr[64], destaddr[64]; - int64_t threshold, nValue, price, totalinputs = 0; - uint256 txid, hashBlock; - //std::vector vopretExtra; - CTransaction vintx; - int32_t j, vout, n = 0; + int64_t threshold, nValue, price, totalinputs = 0; + int32_t n = 0; std::vector > unspentOutputs; + GetNonfungibleData(tokenid, vopretNonfungible); + if (vopretNonfungible.size() > 0) + cp->additionalTokensEvalcode2 = vopretNonfungible.begin()[0]; + GetTokensCCaddress(cp, tokenaddr, pk); SetCCunspents(unspentOutputs, tokenaddr); - threshold = total / (maxinputs != 0 ? maxinputs : 64); // TODO: is maxinputs really could not be over 64? what if i want to calc total balance? + if (unspentOutputs.empty()) { + LOGSTREAM("tokens", CCLOG_INFO, stream << "AddTokenCCInputs() no utxos for token dual/three eval addr=" << tokenaddr << " evalcode=" << (int)cp->evalcode << " additionalTokensEvalcode2=" << (int)cp->additionalTokensEvalcode2 << std::endl); + } + + threshold = total / (maxinputs != 0 ? maxinputs : 64); // TODO: maxinputs really could not be over 64? what if i want to calc total balance for all available uxtos? + // maybe it is better to add all uxtos if maxinputs == 0 for (std::vector >::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) { - txid = it->first.txhash; - vout = (int32_t)it->first.index; - if (it->second.satoshis < threshold) - continue; - for (j = 0; jfirst.txhash; + int32_t vout = (int32_t)it->first.index; + + if (it->second.satoshis < threshold) // this should work also for non-fungible tokens (there should be only 1 satoshi for non-fungible token issue) continue; - if (GetTransaction(txid, vintx, hashBlock, false) != 0) + int32_t ivin; + for (ivin = 0; ivin < mtx.vin.size(); ivin ++) + if (vintxid == mtx.vin[ivin].prevout.hash && vout == mtx.vin[ivin].prevout.n) + break; + if (ivin != mtx.vin.size()) // that is, the tx.vout is already added to mtx.vin (in some previous calls) + continue; + + if (GetTransaction(vintxid, vintx, hashBlock, false) != 0) { Getscriptaddress(destaddr, vintx.vout[vout].scriptPubKey); - if (strcmp(destaddr, tokenaddr) != 0 && strcmp(destaddr, cp->unspendableCCaddr) != 0 && strcmp(destaddr, cp->unspendableaddr2) != 0) + if (strcmp(destaddr, tokenaddr) != 0 && + strcmp(destaddr, cp->unspendableCCaddr) != 0 && // TODO: check why this. Should not we add token inputs from unspendable cc addr if mypubkey is used? + strcmp(destaddr, cp->unspendableaddr2) != 0) // or the logic is to allow to spend all available tokens (what about unspendableaddr3)? continue; - //fprintf(stderr, "AddTokenCCInputs() check destaddress=%s vout amount=%.8f\n", destaddr, (double)vintx.vout[vout].nValue / COIN); - - std::vector vinPubkeys; - if ((nValue = IsTokensvout(true, true/*<--add only checked token uxtos */, cp, NULL, vintx, vout, tokenid)) > 0 && myIsutxo_spentinmempool(txid, vout) == 0) + LOGSTREAM("tokens", CCLOG_DEBUG1, stream << "AddTokenCCInputs() check vintx vout destaddress=" << destaddr << " amount=" << vintx.vout[vout].nValue << std::endl); + + if ((nValue = IsTokensvout(true, true/*<--add only valid token uxtos */, cp, NULL, vintx, vout, tokenid)) > 0 && myIsutxo_spentinmempool(vintxid, vout) == 0) { - if (total != 0 && maxinputs != 0) - mtx.vin.push_back(CTxIn(txid, vout, CScript())); + //for non-fungible tokens check payload: + if (!vopretNonfungible.empty()) { + std::vector vopret; + + // check if it is non-fungible token: + GetNonfungibleData(tokenid, vopret); + if (vopret != vopretNonfungible) { + LOGSTREAM("tokens", CCLOG_INFO, stream << "AddTokenCCInputs() found incorrect non-fungible opret payload for vintxid=" << vintxid.GetHex() << std::endl); + continue; + } + + // non-fungible evalCode2 cc contract should also check if there exists only one non-fungible vout with amount = 1 + } + + + if (total != 0 && maxinputs != 0) // if it is not just to calc amount... + mtx.vin.push_back(CTxIn(vintxid, vout, CScript())); + nValue = it->second.satoshis; totalinputs += nValue; - std::cerr << "AddTokenInputs() adding input nValue=" << nValue << std::endl; + LOGSTREAM("tokens", CCLOG_DEBUG1, stream << "AddTokenCCInputs() adding input nValue=" << nValue << std::endl); n++; + if ((total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs)) break; } } } - //std::cerr << "AddTokenInputs() found totalinputs=" << totalinputs << std::endl; + //std::cerr << "AddTokenCCInputs() found totalinputs=" << totalinputs << std::endl; return(totalinputs); } -std::string CreateToken(int64_t txfee, int64_t assetsupply, std::string name, std::string description) +std::string CreateToken(int64_t txfee, int64_t tokensupply, std::string name, std::string description, std::vector nonfungibleData) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); CPubKey mypk; struct CCcontract_info *cp, C; - if (assetsupply < 0) + if (tokensupply < 0) { - fprintf(stderr, "negative assetsupply %lld\n", (long long)assetsupply); - return(""); + LOGSTREAM("tokens", CCLOG_INFO, stream << "CreateToken() negative tokensupply=" << tokensupply << std::endl); + return std::string(""); } + if (!nonfungibleData.empty() && tokensupply != 1) { + LOGSTREAM("tokens", CCLOG_INFO, stream << "CreateToken() for non-fungible tokens tokensupply should be equal to 1" << std::endl); + CCerror = "for non-fungible tokens tokensupply should be equal to 1"; + return std::string(""); + } + cp = CCinit(&C, EVAL_TOKENS); - if (name.size() > 32 || description.size() > 4096) + if (name.size() > 32 || description.size() > 4096) // this is also checked on rpc level { - fprintf(stderr, "name.%d or description.%d is too big\n", (int32_t)name.size(), (int32_t)description.size()); + LOGSTREAM("tokens", CCLOG_INFO, stream << "name of=" << name.size() << " or description of=" << description.size() << " is too big" << std::endl); + CCerror = "name should be < 32, description should be < 4096"; return(""); } if (txfee == 0) txfee = 10000; mypk = pubkey2pk(Mypubkey()); - if (AddNormalinputs(mtx, mypk, assetsupply + 2 * txfee, 64) > 0) + if (AddNormalinputs(mtx, mypk, tokensupply + 2 * txfee, 64) > 0) { - mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, assetsupply, mypk)); + uint8_t destEvalCode = EVAL_TOKENS; + if( nonfungibleData.size() > 0 ) + destEvalCode = nonfungibleData.begin()[0]; + + mtx.vout.push_back(MakeTokensCC1vout(destEvalCode, tokensupply, mypk)); mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(cp->CChexstr) << OP_CHECKSIG)); - return(FinalizeCCTx(0, cp, mtx, mypk, txfee, EncodeTokenCreateOpRet('c', Mypubkey(), name, description))); + return(FinalizeCCTx(0, cp, mtx, mypk, txfee, EncodeTokenCreateOpRet('c', Mypubkey(), name, description, nonfungibleData))); } - return(""); + + LOGSTREAM("tokens", CCLOG_INFO, stream << "cant find normal inputs" << std::endl); + CCerror = "cant find normal inputs"; + return std::string(""); } - -std::string TokenTransfer(int64_t txfee, uint256 assetid, std::vector destpubkey, int64_t total) +// transfer tokens to another pubkey +// param additionalEvalCode allows transfer of dual-eval non-fungible tokens +std::string TokenTransfer(int64_t txfee, uint256 tokenid, std::vector destpubkey, int64_t total) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); CPubKey mypk; uint64_t mask; int64_t CCchange = 0, inputs = 0; struct CCcontract_info *cp, C; - std::vector emptyExtraOpret; + std::vector vopretNonfungible; - if (total < 0) - { - fprintf(stderr, "negative total %lld\n", (long long)total); + if (total < 0) { + LOGSTREAM("tokens", CCLOG_INFO, stream << "negative total=" << total << std::endl); return(""); } + cp = CCinit(&C, EVAL_TOKENS); + if (txfee == 0) txfee = 10000; mypk = pubkey2pk(Mypubkey()); if (AddNormalinputs(mtx, mypk, txfee, 3) > 0) { - //n = outputs.size(); - //if ( n == amounts.size() ) - //{ - // for (i=0; i 0) + mask = ~((1LL << mtx.vin.size()) - 1); // seems, mask is not used anymore + + if ((inputs = AddTokenCCInputs(cp, mtx, mypk, tokenid, total, 60, vopretNonfungible)) > 0) // NOTE: AddTokenCCInputs might set cp->additionalEvalCode which is used in FinalizeCCtx! { - if (inputs < total) { //added dimxy - std::cerr << "AssetTransfer(): insufficient funds" << std::endl; - return (""); + LOGSTREAM("tokens", CCLOG_INFO, stream << "TokenTransfer(): insufficient token funds" << std::endl); + CCerror = strprintf("insufficient token inputs"); + return std::string(""); } + + uint8_t destEvalCode = EVAL_TOKENS; + if (vopretNonfungible.size() > 0) + destEvalCode = vopretNonfungible.begin()[0]; + if (inputs > total) CCchange = (inputs - total); - //for (i=0; i voutTokenPubkeys; voutTokenPubkeys.push_back(pubkey2pk(destpubkey)); // dest pubkey for validating vout - return(FinalizeCCTx(mask, cp, mtx, mypk, txfee, EncodeTokenOpRet('t', EVAL_TOKENS, assetid, voutTokenPubkeys, CScript()))); // By setting EVAL_TOKENS we're getting out from assets validation code + return(FinalizeCCTx(mask, cp, mtx, mypk, txfee, EncodeTokenOpRet(tokenid, voutTokenPubkeys, vopretNonfungible, CScript()))); } else { - fprintf(stderr, "not enough CC token inputs for %.8f\n", (double)total / COIN); + LOGSTREAM("tokens", CCLOG_INFO, stream << "not enough CC token inputs for amount=" << total << std::endl); + CCerror = strprintf("no token inputs"); } //} else fprintf(stderr,"numoutputs.%d != numamounts.%d\n",n,(int32_t)amounts.size()); } else { - fprintf(stderr, "not enough normal inputs for txfee\n"); + LOGSTREAM("tokens", CCLOG_INFO, stream << "not enough normal inputs for txfee" << std::endl); + CCerror = strprintf("insufficient normal inputs"); } return(""); } @@ -708,7 +864,7 @@ int64_t GetTokenBalance(CPubKey pk, uint256 tokenid) if (GetTransaction(tokenid, tokentx, hashBlock, false) == 0) { - fprintf(stderr, "cant find tokenid\n"); + LOGSTREAM("tokens", CCLOG_INFO, stream << "cant find tokenid" << std::endl); CCerror = strprintf("cant find tokenid"); return 0; } @@ -720,26 +876,35 @@ int64_t GetTokenBalance(CPubKey pk, uint256 tokenid) UniValue TokenInfo(uint256 tokenid) { - UniValue result(UniValue::VOBJ); uint256 hashBlock; CTransaction vintx; std::vector origpubkey; std::string name, description; char str[67], numstr[65]; + UniValue result(UniValue::VOBJ); + uint256 hashBlock; + CTransaction vintx; + std::vector origpubkey; + std::vector vopretNonfungible; + std::string name, description; + if (GetTransaction(tokenid, vintx, hashBlock, false) == 0) { - fprintf(stderr, "cant find assetid\n"); + fprintf(stderr, "TokenInfo() cant find tokenid\n"); result.push_back(Pair("result", "error")); result.push_back(Pair("error", "cant find tokenid")); return(result); } - if (vintx.vout.size() > 0 && DecodeTokenCreateOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, origpubkey, name, description) == 0) + if (vintx.vout.size() > 0 && DecodeTokenCreateOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, origpubkey, name, description, vopretNonfungible) == 0) { - fprintf(stderr, "assetid isnt token creation txid\n"); + LOGSTREAM("tokens", CCLOG_INFO, stream << "TokenInfo() passed tokenid isnt token creation txid" << std::endl); result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "assetid isnt token creation txid")); + result.push_back(Pair("error", "tokenid isnt token creation txid")); } result.push_back(Pair("result", "success")); - result.push_back(Pair("tokenid", uint256_str(str, tokenid))); - result.push_back(Pair("owner", pubkey33_str(str, origpubkey.data()))); + result.push_back(Pair("tokenid", tokenid.GetHex())); + result.push_back(Pair("owner", HexStr(origpubkey))); result.push_back(Pair("name", name)); result.push_back(Pair("supply", vintx.vout[0].nValue)); result.push_back(Pair("description", description)); + if( !vopretNonfungible.empty() ) + result.push_back(Pair("data", HexStr(vopretNonfungible))); + return(result); } diff --git a/src/cc/CCtokens.h b/src/cc/CCtokens.h index e7bb62101..4637ba6c9 100644 --- a/src/cc/CCtokens.h +++ b/src/cc/CCtokens.h @@ -28,14 +28,15 @@ // CCcustom bool TokensValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cpTokens, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 tokenid); -//int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *cp, Eval* eval, std::vector &origpubkey, const CTransaction& tx, int32_t v, uint256 reftokenid, std::vector vinPubkeys); -std::string CreateToken(int64_t txfee, int64_t assetsupply, std::string name, std::string description); +std::string CreateToken(int64_t txfee, int64_t assetsupply, std::string name, std::string description, std::vector nonfungibleData); std::string TokenTransfer(int64_t txfee, uint256 assetid, std::vector destpubkey, int64_t total); 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); diff --git a/src/cc/CCtx.cpp b/src/cc/CCtx.cpp index d93185ca4..6536fdee3 100644 --- a/src/cc/CCtx.cpp +++ b/src/cc/CCtx.cpp @@ -46,8 +46,8 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran CTransaction vintx; std::string hex; CPubKey globalpk; 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,flag,utxovout,n,err = 0; - char myaddr[64], destaddr[64], unspendable[64], mytokensaddr[64], mysingletokensaddr[64], tokensunspendable[64]; - uint8_t *privkey, myprivkey[32], unspendablepriv[32], tokensunspendablepriv[32], *msg32 = 0; + char myaddr[64], destaddr[64], unspendable[64], mytokensaddr[64], mysingletokensaddr[64], unspendabletokensaddr[64]; + uint8_t *privkey, myprivkey[32], unspendablepriv[32], /*tokensunspendablepriv[32],*/ *msg32 = 0; CC *mycond=0, *othercond=0, *othercond2=0,*othercond4=0, *othercond3=0, *othercond1of2=NULL, *othercond1of2tokens = NULL, *cond, *mytokenscond = NULL, *mysingletokenscond = NULL, *othertokenscond = NULL; CPubKey unspendablepk /*, tokensunspendablepk*/; struct CCcontract_info *cpTokens, tokensC; @@ -69,26 +69,26 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran GetCCaddress(cp,myaddr,mypk); mycond = MakeCCcond1(cp->evalcode,mypk); - // to spend from single-eval evalcode 'unspendable' + // to spend from single-eval evalcode 'unspendable' cc addr unspendablepk = GetUnspendable(cp, unspendablepriv); GetCCaddress(cp, unspendable, unspendablepk); othercond = MakeCCcond1(cp->evalcode, unspendablepk); //printf("evalcode.%d (%s)\n",cp->evalcode,unspendable); - // tokens support: - // to spend from dual-eval mypk vout + // tokens support: + // to spend from dual/three-eval mypk vout GetTokensCCaddress(cp, mytokensaddr, mypk); - mytokenscond = MakeTokensCCcond1(cp->evalcode, mypk); + // NOTE: if additionalEvalcode2 is not set it is a dual-eval (not three-eval) cc cond: + mytokenscond = MakeTokensCCcond1(cp->evalcode, cp->additionalTokensEvalcode2, mypk); // to spend from single-eval EVAL_TOKENS mypk cpTokens = CCinit(&tokensC, EVAL_TOKENS); GetCCaddress(cpTokens, mysingletokensaddr, mypk); mysingletokenscond = MakeCCcond1(EVAL_TOKENS, mypk); - // to spend from dual-eval EVAL_TOKEN+evalcode 'unspendable' pk - //tokensunspendablepk = GetUnspendable(cpTokens, tokensunspendablepriv); - GetTokensCCaddress(cp, tokensunspendable, unspendablepk); - othertokenscond = MakeTokensCCcond1(cp->evalcode, unspendablepk); + // to spend from dual/three-eval EVAL_TOKEN+evalcode 'unspendable' pk: + GetTokensCCaddress(cp, unspendabletokensaddr, unspendablepk); // it may be a three-eval cc, if cp->additionalEvalcode2 is set + othertokenscond = MakeTokensCCcond1(cp->evalcode, cp->additionalTokensEvalcode2, 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. @@ -155,9 +155,10 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran { Getscriptaddress(destaddr,vintx.vout[utxovout].scriptPubKey); //fprintf(stderr,"FinalizeCCTx() vin.%d is CC %.8f -> (%s)\n",i,(double)utxovalues[i]/COIN,destaddr); - //std::cerr << "FinalizeCCtx() searching destaddr=" << destaddr << " myaddr=" << myaddr << std::endl; - if( strcmp(destaddr,myaddr) == 0 ) + std::cerr << "FinalizeCCtx() searching destaddr=" << destaddr << " for vin[" << i << "] satoshis=" << utxovalues[i] << std::endl; + if( strcmp(destaddr, myaddr) == 0 ) { + fprintf(stderr, "FinalizeCCTx() matched cc myaddr (%s)\n", myaddr); privkey = myprivkey; cond = mycond; } @@ -165,48 +166,48 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran { privkey = myprivkey; cond = mytokenscond; - //fprintf(stderr,"FinalizeCCTx() matched dual-eval TokensCC1vout CC addr.(%s)\n",mytokensaddr); + fprintf(stderr,"FinalizeCCTx() matched dual-eval TokensCC1vout my token addr.(%s)\n",mytokensaddr); } else if (strcmp(destaddr, mysingletokensaddr) == 0) // if this is TokensCC1vout { privkey = myprivkey; cond = mysingletokenscond; - //fprintf(stderr, "FinalizeCCTx() matched single-eval token CC1vout CC addr.(%s)\n", mytokensaddr); + fprintf(stderr, "FinalizeCCTx() matched single-eval token CC1vout my token addr.(%s)\n", mytokensaddr); } else if ( strcmp(destaddr,unspendable) == 0 ) { privkey = unspendablepriv; cond = othercond; - //fprintf(stderr,"FinalizeCCTx(%d) matched unspendable CC addr.(%s)\n",cp->evalcode,unspendable); + fprintf(stderr,"FinalizeCCTx evalcode(%d) matched unspendable CC addr.(%s)\n",cp->evalcode,unspendable); } - else if (strcmp(destaddr, tokensunspendable) == 0) + else if (strcmp(destaddr, unspendabletokensaddr) == 0) { privkey = unspendablepriv; cond = othertokenscond; - //fprintf(stderr,"FinalizeCCTx() matched tokensunspendable CC addr.(%s)\n",unspendable); + fprintf(stderr,"FinalizeCCTx() matched unspendabletokensaddr dual/three-eval CC addr.(%s)\n",unspendabletokensaddr); } // check if this is the 2nd additional evalcode + 'unspendable' cc addr: - else if ( strcmp(destaddr,cp->unspendableaddr2) == 0) + else if ( strcmp(destaddr, cp->unspendableaddr2) == 0) { - //fprintf(stderr,"FinalizeCCTx() matched %s unspendable2!\n",cp->unspendableaddr2); + fprintf(stderr,"FinalizeCCTx() matched %s unspendable2!\n",cp->unspendableaddr2); privkey = cp->unspendablepriv2; - if ( othercond2 == 0 ) - othercond2 = MakeCCcond1(cp->evalcode2, cp->unspendablepk2); + if( othercond2 == 0 ) + othercond2 = MakeCCcond1(cp->unspendableEvalcode2, cp->unspendablepk2); cond = othercond2; } // check if this is 3rd additional evalcode + 'unspendable' cc addr: else if ( strcmp(destaddr,cp->unspendableaddr3) == 0 ) { - //fprintf(stderr,"FinalizeCCTx() matched %s unspendable3!\n",cp->unspendableaddr3); + fprintf(stderr,"FinalizeCCTx() matched %s unspendable3!\n",cp->unspendableaddr3); privkey = cp->unspendablepriv3; - if ( othercond3 == 0 ) - othercond3 = MakeCCcond1(cp->evalcode3,cp->unspendablepk3); + if( othercond3 == 0 ) + othercond3 = MakeCCcond1(cp->unspendableEvalcode3, cp->unspendablepk3); cond = othercond3; } // check if this is spending from 1of2 cc coins addr: else if (strcmp(cp->coins1of2addr, destaddr) == 0) { - //fprintf(stderr,"FinalizeCCTx() matched %s unspendable1of2!\n",cp->coins1of2addr); + fprintf(stderr,"FinalizeCCTx() matched %s unspendable1of2!\n",cp->coins1of2addr); privkey = myprivkey; if (othercond1of2 == 0) othercond1of2 = MakeCCcond1of2(cp->evalcode, cp->coins1of2pk[0], cp->coins1of2pk[1]); @@ -215,10 +216,12 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran // check if this is spending from 1of2 cc tokens addr: else if (strcmp(cp->tokens1of2addr, destaddr) == 0) { - //fprintf(stderr,"FinalizeCCTx() matched %s cp->tokens1of2addr!\n", cp->tokens1of2addr); + fprintf(stderr,"FinalizeCCTx() matched %s cp->tokens1of2addr!\n", cp->tokens1of2addr); privkey = myprivkey; if (othercond1of2tokens == 0) - othercond1of2tokens = MakeTokensCCcond1of2(cp->evalcode, cp->tokens1of2pk[0], cp->tokens1of2pk[1]); + // NOTE: if additionalEvalcode2 is not set then it is dual-eval cc else three-eval cc + // TODO: verify evalcodes order if additionalEvalcode2 is not 0 + othercond1of2tokens = MakeTokensCCcond1of2(cp->evalcode, cp->additionalTokensEvalcode2, cp->tokens1of2pk[0], cp->tokens1of2pk[1]); cond = othercond1of2tokens; } else diff --git a/src/cc/CCutils.cpp b/src/cc/CCutils.cpp index 6dff5d3cd..6892f6fab 100644 --- a/src/cc/CCutils.cpp +++ b/src/cc/CCutils.cpp @@ -76,7 +76,8 @@ CTxOut MakeCC1of2vout(uint8_t evalcode,CAmount nValue,CPubKey pk1,CPubKey pk2) return(vout); } -CC *MakeTokensCCcond1of2(uint8_t evalcode, CPubKey pk1, CPubKey pk2) +// make three-eval (token+evalcode+evalcode2) 1of2 cryptocondition: +CC *MakeTokensCCcond1of2(uint8_t evalcode, uint8_t evalcode2, CPubKey pk1, CPubKey pk2) { // make 1of2 sigs cond std::vector pks; @@ -85,44 +86,68 @@ CC *MakeTokensCCcond1of2(uint8_t evalcode, CPubKey pk1, CPubKey pk2) std::vector thresholds; thresholds.push_back( CCNewEval(E_MARSHAL(ss << evalcode)) ); - if( evalcode != EVAL_TOKENS ) // if evalCode == EVAL_TOKENS, it is actually MakeCCcond1of2()! - thresholds.push_back(CCNewEval(E_MARSHAL(ss << (uint8_t)EVAL_TOKENS))); // this is eval token cc - thresholds.push_back(CCNewThreshold(1, pks)); // this is 1 of 2 sigs cc + if( evalcode != EVAL_TOKENS ) // if evalCode == EVAL_TOKENS, it is actually MakeCCcond1of2()! + thresholds.push_back(CCNewEval(E_MARSHAL(ss << (uint8_t)EVAL_TOKENS))); // this is eval token cc + if( evalcode2 != 0 ) + thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode2))); // add optional additional evalcode + thresholds.push_back(CCNewThreshold(1, pks)); // this is 1 of 2 sigs cc return CCNewThreshold(thresholds.size(), thresholds); } +// overload to make two-eval (token+evalcode) 1of2 cryptocondition: +CC *MakeTokensCCcond1of2(uint8_t evalcode, CPubKey pk1, CPubKey pk2) { + return MakeTokensCCcond1of2(evalcode, 0, pk1, pk2); +} -CC *MakeTokensCCcond1(uint8_t evalcode, CPubKey pk) +// make three-eval (token+evalcode+evalcode2) cryptocondition: +CC *MakeTokensCCcond1(uint8_t evalcode, uint8_t evalcode2, CPubKey pk) { std::vector pks; pks.push_back(CCNewSecp256k1(pk)); std::vector thresholds; thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode))); - if (evalcode != EVAL_TOKENS) // if evalCode == EVAL_TOKENS, it is actually MakeCCcond1()! - thresholds.push_back(CCNewEval(E_MARSHAL(ss << (uint8_t)EVAL_TOKENS))); // this is eval token cc - thresholds.push_back(CCNewThreshold(1, pks)); // signature + if (evalcode != EVAL_TOKENS) // if evalCode == EVAL_TOKENS, it is actually MakeCCcond1()! + thresholds.push_back(CCNewEval(E_MARSHAL(ss << (uint8_t)EVAL_TOKENS))); // this is eval token cc + if (evalcode2 != 0) + thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode2))); // add optional additional evalcode + thresholds.push_back(CCNewThreshold(1, pks)); // signature return CCNewThreshold(thresholds.size(), thresholds); } +// overload to make two-eval (token+evalcode) cryptocondition: +CC *MakeTokensCCcond1(uint8_t evalcode, CPubKey pk) { + return MakeTokensCCcond1(evalcode, 0, pk); +} -CTxOut MakeTokensCC1of2vout(uint8_t evalcode, CAmount nValue, CPubKey pk1, CPubKey pk2) +// make three-eval (token+evalcode+evalcode2) 1of2 cc vout: +CTxOut MakeTokensCC1of2vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk1, CPubKey pk2) { CTxOut vout; - CC *payoutCond = MakeTokensCCcond1of2(evalcode, pk1, pk2); + CC *payoutCond = MakeTokensCCcond1of2(evalcode, evalcode2, pk1, pk2); vout = CTxOut(nValue, CCPubKey(payoutCond)); cc_free(payoutCond); return(vout); } +// overload to make two-eval (token+evalcode) 1of2 cc vout: +CTxOut MakeTokensCC1of2vout(uint8_t evalcode, CAmount nValue, CPubKey pk1, CPubKey pk2) { + return MakeTokensCC1of2vout(evalcode, 0, nValue, pk1, pk2); +} -CTxOut MakeTokensCC1vout(uint8_t evalcode, CAmount nValue, CPubKey pk) +// make three-eval (token+evalcode+evalcode2) cc vout: +CTxOut MakeTokensCC1vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk) { CTxOut vout; - CC *payoutCond = MakeTokensCCcond1(evalcode, pk); + CC *payoutCond = MakeTokensCCcond1(evalcode, evalcode2, pk); vout = CTxOut(nValue, CCPubKey(payoutCond)); cc_free(payoutCond); return(vout); } +// overload to make two-eval (token+evalcode) cc vout: +CTxOut MakeTokensCC1vout(uint8_t evalcode, CAmount nValue, CPubKey pk) { + return MakeTokensCC1vout(evalcode, 0, nValue, pk); +} + CC* GetCryptoCondition(CScript const& scriptSig) { @@ -227,17 +252,19 @@ CPubKey pubkey2pk(std::vector pubkey) return(pk); } +// set additional 'unspendable' addr void CCaddr2set(struct CCcontract_info *cp,uint8_t evalcode,CPubKey pk,uint8_t *priv,char *coinaddr) { - cp->evalcode2 = evalcode; + cp->unspendableEvalcode2 = evalcode; cp->unspendablepk2 = pk; memcpy(cp->unspendablepriv2,priv,32); strcpy(cp->unspendableaddr2,coinaddr); } +// set yet another additional 'unspendable' addr void CCaddr3set(struct CCcontract_info *cp,uint8_t evalcode,CPubKey pk,uint8_t *priv,char *coinaddr) { - cp->evalcode3 = evalcode; + cp->unspendableEvalcode3 = evalcode; cp->unspendablepk3 = pk; memcpy(cp->unspendablepriv3,priv,32); strcpy(cp->unspendableaddr3,coinaddr); @@ -251,12 +278,13 @@ void CCaddr1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, char *c strcpy(cp->coins1of2addr, coinaddr); } -// set pubkeys, myprivkey and 1of2 cc addr for spending from 1of2 tokens cryptocondition vout: -void CCaddrTokens1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, char *coinaddr) +// set pubkeys, myprivkey and 1of2 cc addr for spending from 1of2 token cryptocondition vout +// to get tokenaddr use GetTokensCCaddress() +void CCaddrTokens1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, char *tokenaddr) { cp->tokens1of2pk[0] = pk1; cp->tokens1of2pk[1] = pk2; - strcpy(cp->tokens1of2addr, coinaddr); + strcpy(cp->tokens1of2addr, tokenaddr); } bool Getscriptaddress(char *destaddr,const CScript &scriptPubKey) @@ -351,11 +379,11 @@ 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) +bool _GetTokensCCaddress(char *destaddr, uint8_t evalcode, uint8_t evalcode2, CPubKey pk) { CC *payoutCond; destaddr[0] = 0; - if ((payoutCond = MakeTokensCCcond1(evalcode, pk)) != 0) + if ((payoutCond = MakeTokensCCcond1(evalcode, evalcode2, pk)) != 0) { Getscriptaddress(destaddr, CCPubKey(payoutCond)); cc_free(payoutCond); @@ -363,12 +391,13 @@ bool _GetTokensCCaddress(char *destaddr, uint8_t evalcode, CPubKey pk) return(destaddr[0] != 0); } +// get scriptPubKey adddress for three/dual eval token cc vout 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)); + return(_GetTokensCCaddress(destaddr, cp->evalcode, cp->additionalTokensEvalcode2, pk)); } @@ -384,11 +413,12 @@ bool GetCCaddress1of2(struct CCcontract_info *cp,char *destaddr,CPubKey pk,CPubK return(destaddr[0] != 0); } +// get scriptPubKey adddress for three/dual eval token 1of2 cc vout bool GetTokensCCaddress1of2(struct CCcontract_info *cp, char *destaddr, CPubKey pk, CPubKey pk2) { CC *payoutCond; destaddr[0] = 0; - if ((payoutCond = MakeTokensCCcond1of2(cp->evalcode, pk, pk2)) != 0) + if ((payoutCond = MakeTokensCCcond1of2(cp->evalcode, cp->additionalTokensEvalcode2, pk, pk2)) != 0) // if additionalTokensEvalcode2 not set then it is dual-eval cc else three-eval cc { Getscriptaddress(destaddr, CCPubKey(payoutCond)); cc_free(payoutCond); @@ -515,7 +545,7 @@ CPubKey GetUnspendable(struct CCcontract_info *cp,uint8_t *unspendablepriv) void CCclearvars(struct CCcontract_info *cp) { - cp->evalcode2 = cp->evalcode3 = 0; + cp->unspendableEvalcode2 = cp->unspendableEvalcode3 = 0; cp->unspendableaddr2[0] = cp->unspendableaddr3[0] = 0; } diff --git a/src/cc/assets.cpp b/src/cc/assets.cpp index 6aae09dd3..97b01302c 100644 --- a/src/cc/assets.cpp +++ b/src/cc/assets.cpp @@ -14,6 +14,7 @@ ******************************************************************************/ #include "CCassets.h" +#include "CCtokens.h" /* Assets can be created or transferred. @@ -129,21 +130,21 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransaction &tx, uint32_t nIn) { static uint256 zero; - CTxDestination address; CTransaction vinTx,createTx; uint256 hashBlock,assetid,assetid2; - int32_t i,starti,numvins,numvouts,preventCCvins,preventCCvouts; - int64_t remaining_price,nValue,assetoshis,outputs,inputs,tmpprice,totalunits,ignore; std::vector origpubkey,tmporigpubkey,ignorepubkey; + CTxDestination address; + CTransaction vinTx, createTx; + uint256 hashBlock, assetid, assetid2; + int32_t i,starti, numvins, numvouts, preventCCvins, preventCCvouts; + int64_t remaining_price, nValue, assetoshis, outputsDummy,inputs,tmpprice,totalunits,ignore; + std::vector origpubkey, tmporigpubkey, ignorepubkey, vopretNonfungible, vopretNonfungibleDummy; uint8_t funcid, evalCodeInOpret; - char destaddr[64], origaddr[64], assetsCCaddr[64], userTokensCCaddr[64]; //, signleEvalTokensCCaddr[64]; + char destaddr[64], origNormalAddr[64], origTokensCCaddr[64], origCCaddrDummy[64]; + char tokensDualEvalUnspendableCCaddr[64], origAssetsCCaddr[64]; //return true; - //CPubKey unspendableTokensPk = GetUnspendable(cpTokens, NULL); - //CPubKey unspendableAssetsPk = GetUnspendable(cpAssets, NULL); - //GetCCaddress(cpTokens, tokensUnspendableCCaddr, unspendableTokensPk); - numvins = tx.vin.size(); numvouts = tx.vout.size(); - outputs = inputs = 0; + outputsDummy = inputs = 0; preventCCvins = preventCCvouts = -1; // add specific chains exceptions for old token support: @@ -156,13 +157,18 @@ 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)) == 0 ) + if((funcid = DecodeAssetTokenOpRet(tx.vout[numvouts-1].scriptPubKey, evalCodeInOpret, assetid, assetid2, remaining_price, origpubkey, vopretNonfungibleDummy)) == 0 ) return eval->Invalid("AssetValidate: invalid opreturn payload"); + // non-fungible tokens support: + GetNonfungibleData(assetid, vopretNonfungible); + if (vopretNonfungible.size() > 0) + cpAssets->additionalTokensEvalcode2 = vopretNonfungible.begin()[0]; + // find dual-eval tokens unspendable addr: - char tokensUnspendableAddr[64],origpubkeyCCaddr[64]; - GetTokensCCaddress(cpAssets, tokensUnspendableAddr, GetUnspendable(cpAssets, NULL)); - GetCCaddress(cpAssets, origpubkeyCCaddr, origpubkey); + GetTokensCCaddress(cpAssets, tokensDualEvalUnspendableCCaddr, GetUnspendable(cpAssets, NULL)); + // this is for marker validation: + GetCCaddress(cpAssets, origAssetsCCaddr, origpubkey); // we need this for validating single-eval tokens' vins/vous: struct CCcontract_info *cpTokens, tokensC; @@ -177,7 +183,7 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti return eval->Invalid("cant find asset create txid"); else if( funcid != 'o' && funcid != 'x' && assetid2 != zero && eval->GetTxUnconfirmed(assetid2, createTx, hashBlock) == 0 ) return eval->Invalid("cant find asset2 create txid"); - else if( IsCCInput(tx.vin[0].scriptSig) != 0 ) + else if( IsCCInput(tx.vin[0].scriptSig) != 0 ) // vin0 should be normal vin return eval->Invalid("illegal asset vin0"); else if( numvouts < 2 ) return eval->Invalid("too few vouts"); // it was if(numvouts < 1) but it refers at least to vout[1] below @@ -191,7 +197,7 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti if( assetid == zero ) return eval->Invalid("illegal assetid"); - else if (!AssetCalcAmounts(cpAssets, inputs, outputs, eval, tx, assetid)) { // Only set inputs and outputs. NOTE: we do not need to check cc inputs == cc outputs + else if (!AssetCalcAmounts(cpAssets, inputs, outputsDummy/*outputsDummy is calculated incorrectly but not used*/, eval, tx, assetid)) { // Only set inputs and outputs. NOTE: we do not need to check cc inputs == cc outputs return false; // returns false if some problems with reading vintxes } } @@ -226,9 +232,13 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti //vout.1: CC output for marker //vout.2: normal output for change (if any) // vout.n-1: opreturn [EVAL_ASSETS] ['b'] [assetid] [amount of asset required] [origpubkey] + + // as we don't use tokenconvert we should not be here: + return eval->Invalid("invalid asset funcid (b)"); + if( remaining_price == 0 ) return eval->Invalid("illegal null amount for buyoffer"); - else if( ConstrainVout(tx.vout[0],1,cpAssets->unspendableCCaddr,0) == 0 ) + else if( ConstrainVout(tx.vout[0], 1, cpAssets->unspendableCCaddr,0) == 0 ) // coins to assets unspendable cc addr return eval->Invalid("invalid vout for buyoffer"); preventCCvins = 1; preventCCvouts = 1; @@ -243,13 +253,13 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti //vout.1: vin.2 back to users pubkey //vout.2: normal output for change (if any) //vout.n-1: opreturn [EVAL_ASSETS] ['o'] - if( (nValue= AssetValidateBuyvin(cpAssets, eval, tmpprice, tmporigpubkey, assetsCCaddr, origaddr, tx, assetid)) == 0 ) + if( (nValue= AssetValidateBuyvin(cpAssets, eval, tmpprice, tmporigpubkey, origCCaddrDummy, origNormalAddr, tx, assetid)) == 0 ) return(false); - else if( ConstrainVout(tx.vout[0],0, origaddr, nValue) == 0 ) + else if( ConstrainVout(tx.vout[0],0, origNormalAddr, nValue) == 0 ) return eval->Invalid("invalid refund for cancelbuy"); preventCCvins = 3; preventCCvouts = 0; - fprintf(stderr,"cancelbuy validated to origaddr.(%s)\n",origaddr); + fprintf(stderr,"cancelbuy validated to origaddr.(%s)\n",origNormalAddr); break; case 'B': // fillbuy: @@ -264,7 +274,7 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti //vout.n-1: opreturn [EVAL_ASSETS] ['B'] [assetid] [remaining asset required] [origpubkey] preventCCvouts = 4; - if( (nValue = AssetValidateBuyvin(cpAssets, eval, totalunits, tmporigpubkey, assetsCCaddr, origaddr, tx, assetid)) == 0 ) + if( (nValue = AssetValidateBuyvin(cpAssets, eval, totalunits, tmporigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 ) return(false); else if( numvouts < 4 ) return eval->Invalid("not enough vouts for fillbuy"); @@ -274,24 +284,24 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti { if( nValue != tx.vout[0].nValue + tx.vout[1].nValue ) return eval->Invalid("locked value doesnt match vout0+1 fillbuy"); - else if( tx.vout[4].scriptPubKey.IsPayToCryptoCondition() != 0 ) + else if( tx.vout[4].scriptPubKey.IsPayToCryptoCondition() != 0 ) // if cc change present { - if( ConstrainVout(tx.vout[2], 1, assetsCCaddr, 0) == 0 ) // tokens on user cc addr + if( ConstrainVout(tx.vout[2], 1, origTokensCCaddr, 0) == 0 ) // tokens to originator cc addr (tokens+nonfungible evals) return eval->Invalid("vout2 doesnt go to origpubkey fillbuy"); else if ( inputs != tx.vout[2].nValue + tx.vout[4].nValue ) return eval->Invalid("asset inputs doesnt match vout2+3 fillbuy"); } - else if( ConstrainVout(tx.vout[2], 1, assetsCCaddr, inputs) == 0 ) // tokens on user cc addr + else if( ConstrainVout(tx.vout[2], 1, origTokensCCaddr, inputs) == 0 ) // tokens to originator cc addr (tokens+nonfungible evals) return eval->Invalid("vout2 doesnt match inputs fillbuy"); - else if( ConstrainVout(tx.vout[1],0,0,0) == 0 ) + else if( ConstrainVout(tx.vout[1], 0, NULL, 0) == 0 ) return eval->Invalid("vout1 is CC for fillbuy"); - else if( ConstrainVout(tx.vout[3], 1, origpubkeyCCaddr, 10000) == 0 ) + else if( ConstrainVout(tx.vout[3], 1, origAssetsCCaddr, 10000) == 0 ) // marker to asset cc addr return eval->Invalid("invalid marker for original pubkey"); else if( ValidateBidRemainder(remaining_price, tx.vout[0].nValue, nValue, tx.vout[1].nValue, tx.vout[2].nValue, totalunits) == false ) return eval->Invalid("mismatched remainder for fillbuy"); else if( remaining_price != 0 ) { - if( ConstrainVout(tx.vout[0], 1, cpAssets->unspendableCCaddr, 0) == 0 ) // coins on asset unspendable cc addr + if( ConstrainVout(tx.vout[0], 1, cpAssets->unspendableCCaddr, 0) == 0 ) // coins to asset unspendable cc addr return eval->Invalid("mismatched vout0 AssetsCCaddr for fillbuy"); } } @@ -308,25 +318,30 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti //vout.3: normal output for change (if any) //'s'.vout.n-1: opreturn [EVAL_ASSETS] ['s'] [assetid] [amount of native coin required] [origpubkey] //'e'.vout.n-1: opreturn [EVAL_ASSETS] ['e'] [assetid] [assetid2] [amount of asset2 required] [origpubkey] + + // as we don't use tokenconvert we should not be here: + return eval->Invalid("invalid asset funcid (s)"); + preventCCvouts = 2; if( remaining_price == 0 ) return eval->Invalid("illegal null remaining_price for selloffer"); if ( tx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 ) return eval->Invalid("invalid normal vout1 for sellvin"); - if( tx.vout[2].scriptPubKey.IsPayToCryptoCondition() != 0 ) // cc change + if( tx.vout[2].scriptPubKey.IsPayToCryptoCondition() != 0 ) // if cc change presents { preventCCvouts++; - if( ConstrainVout(tx.vout[0], 1, (char *)cpTokens->unspendableCCaddr, 0) == 0 ) // check also cc vout[0] + if( ConstrainVout(tx.vout[0], 1, (char *)cpTokens->unspendableCCaddr, 0) == 0 ) // tokens to tokens unspendable cc addr. TODO: this in incorrect, should be assets if we got here! return eval->Invalid("mismatched vout0 TokensCCaddr for selloffer"); else if( tx.vout[0].nValue + tx.vout[2].nValue != inputs ) return eval->Invalid("mismatched vout0+vout2 total for selloffer"); } - else if( ConstrainVout(tx.vout[0], 1, (char *)cpTokens->unspendableCCaddr, inputs) == 0 ) // no cc change, just vout[0] + // no cc change: + else if( ConstrainVout(tx.vout[0], 1, (char *)cpTokens->unspendableCCaddr, inputs) == 0 ) // tokens to tokens unspendable cc addr TODO: this in incorrect, should be assets if got here! return eval->Invalid("mismatched vout0 TokensCCaddr for selloffer"); //fprintf(stderr,"remaining.%d for sell\n",(int32_t)remaining_price); break; - case 'x': // cancel + case 'x': // cancel sell //vin.0: normal input //vin.1: unspendable.(vout.0 from exchange or selloffer) sellTx/exchangeTx.vout[0] inputTx //vin.2: CC marker from selloffer for txfee @@ -335,9 +350,9 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti //vout.2: normal output for change (if any) //vout.n-1: opreturn [EVAL_ASSETS] ['x'] [assetid] - if( (assetoshis = AssetValidateSellvin(cpAssets, eval, tmpprice, tmporigpubkey, userTokensCCaddr, origaddr, tx, assetid)) == 0 ) // NOTE: + if( (assetoshis = AssetValidateSellvin(cpAssets, eval, tmpprice, tmporigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 ) // NOTE: return(false); - else if( ConstrainVout(tx.vout[0], 1, userTokensCCaddr, assetoshis) == 0 ) + else if( ConstrainVout(tx.vout[0], 1, origTokensCCaddr, assetoshis) == 0 ) // tokens returning to originator cc addr return eval->Invalid("invalid vout for cancel"); preventCCvins = 3; preventCCvouts = 1; @@ -353,7 +368,7 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti //vout.3: normal output for change (if any) //'S'.vout.n-1: opreturn [EVAL_ASSETS] ['S'] [assetid] [amount of coin still required] [origpubkey] - if( (assetoshis = AssetValidateSellvin(cpAssets, eval, totalunits, tmporigpubkey, userTokensCCaddr, origaddr, tx, assetid)) == 0 ) + if( (assetoshis = AssetValidateSellvin(cpAssets, eval, totalunits, tmporigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 ) return(false); else if( numvouts < 4 ) return eval->Invalid("not enough vouts for fillask"); @@ -365,17 +380,15 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti return eval->Invalid("locked value doesnt match vout0+1 fillask"); if( ValidateAskRemainder(remaining_price, tx.vout[0].nValue, assetoshis, tx.vout[1].nValue, tx.vout[2].nValue, totalunits) == false ) return eval->Invalid("mismatched remainder for fillask"); - else if( ConstrainVout(tx.vout[1], 1, 0, 0) == 0 ) + else if( ConstrainVout(tx.vout[1], 1, NULL, 0) == 0 ) // do not check token buyer's cc addr return eval->Invalid("normal vout1 for fillask"); - else if( ConstrainVout(tx.vout[2], 0, origaddr, 0) == 0 ) + else if( ConstrainVout(tx.vout[2], 0, origNormalAddr, 0) == 0 ) // coins to originator normal addr return eval->Invalid("normal vout1 for fillask"); - else if( ConstrainVout(tx.vout[3], 1, origpubkeyCCaddr, 10000) == 0 ) + else if( ConstrainVout(tx.vout[3], 1, origAssetsCCaddr, 10000) == 0 ) // marker to originator asset cc addr return eval->Invalid("invalid marker for original pubkey"); else if( remaining_price != 0 ) { - //char tokensUnspendableAddr[64]; - //GetTokensCCaddress(cpAssets, tokensUnspendableAddr, GetUnspendable(cpAssets, NULL)); - if ( ConstrainVout(tx.vout[0], 1, tokensUnspendableAddr /*(char *)cpAssets->unspendableCCaddr*/, 0) == 0 ) + if ( ConstrainVout(tx.vout[0], 1, tokensDualEvalUnspendableCCaddr, 0) == 0 ) return eval->Invalid("mismatched vout0 assets dual unspendable CCaddr for fill sell"); } } @@ -399,7 +412,7 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti // eval->Invalid("asset2 inputs != outputs"); ////////// not implemented yet //////////// - if( (assetoshis= AssetValidateSellvin(cpTokens, eval, totalunits, tmporigpubkey, userTokensCCaddr, origaddr, tx, assetid)) == 0 ) + if( (assetoshis= AssetValidateSellvin(cpTokens, eval, totalunits, tmporigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 ) return(false); else if( numvouts < 3 ) return eval->Invalid("not enough vouts for fillex"); @@ -412,7 +425,7 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti else if( tx.vout[3].scriptPubKey.IsPayToCryptoCondition() != 0 ) ////////// not implemented yet //////////// { - if( ConstrainVout(tx.vout[2], 1, userTokensCCaddr, 0) == 0 ) + if( ConstrainVout(tx.vout[2], 1, origTokensCCaddr, 0) == 0 ) return eval->Invalid("vout2 doesnt go to origpubkey fillex"); else if( inputs != tx.vout[2].nValue + tx.vout[3].nValue ) { @@ -421,7 +434,7 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti } } ////////// not implemented yet //////////// - else if( ConstrainVout(tx.vout[2], 1, userTokensCCaddr, inputs) == 0 ) + else if( ConstrainVout(tx.vout[2], 1, origTokensCCaddr, inputs) == 0 ) return eval->Invalid("vout2 doesnt match inputs fillex"); else if( ConstrainVout(tx.vout[1], 0, 0, 0) == 0 ) return eval->Invalid("vout1 is CC for fillex"); diff --git a/src/cc/oracles.cpp b/src/cc/oracles.cpp index 66c0e1b9a..a1d349c12 100644 --- a/src/cc/oracles.cpp +++ b/src/cc/oracles.cpp @@ -171,7 +171,7 @@ CPubKey OracleBatonPk(char *batonaddr,struct CCcontract_info *cp) if ( ctx == 0 ) ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); Myprivkey(priv); - cp->evalcode2 = EVAL_ORACLES; + cp->unspendableEvalcode2 = EVAL_ORACLES; for (i=0; i<32; i++) cp->unspendablepriv2[i] = (priv[i] ^ cp->CCpriv[i]); while ( secp256k1_ec_seckey_verify(ctx,cp->unspendablepriv2) == 0 ) diff --git a/src/cc/prices.cpp b/src/cc/prices.cpp index 369495b1a..03a4d2b9c 100644 --- a/src/cc/prices.cpp +++ b/src/cc/prices.cpp @@ -13,6 +13,7 @@ * * ******************************************************************************/ +#include "CCassets.h" #include "CCPrices.h" /* @@ -89,6 +90,9 @@ uint8_t DecodePricesFundingOpRet(CScript scriptPubKey,CPubKey &planpk,uint256 &o bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64]; + + return true; // TODO remove, for test dual-evals + return eval->Invalid("no validation yet"); std::vector > txids; numvins = tx.vin.size(); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index f8fe7de20..abd0b9696 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -7038,75 +7038,98 @@ UniValue tokenbalance(const UniValue& params, bool fHelp) UniValue tokencreate(const UniValue& params, bool fHelp) { - UniValue result(UniValue::VOBJ); std::string name,description,hex; uint64_t supply; - if ( fHelp || params.size() > 3 || params.size() < 2 ) - throw runtime_error("tokencreate name supply description\n"); + UniValue result(UniValue::VOBJ); + std::string name, description, hextx; + std::vector nonfungibleData; + int64_t supply; // changed from uin64_t to int64_t for this 'if ( supply <= 0 )' to work as expected + + CCerror.clear(); + + if ( fHelp || params.size() > 4 || params.size() < 2 ) + throw runtime_error("tokencreate name supply [description][data]\n"); if ( ensure_CCrequirements() < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); + name = params[0].get_str(); - supply = atof(params[1].get_str().c_str()) * COIN + 0.00000000499999; // what for is this '+0.00000000499999'? it will be lost while converting double to int64_t (dimxy) - if ( name.size() == 0 || name.size() > 32) - { + if (name.size() == 0 || name.size() > 32) { ERR_RESULT("Token name must not be empty and up to 32 characters"); return(result); } - if ( supply <= 0 ) - { + + supply = atof(params[1].get_str().c_str()) * COIN + 0.00000000499999; // what for is this '+0.00000000499999'? it will be lost while converting double to int64_t (dimxy) + if (supply <= 0) { ERR_RESULT("Token supply must be positive"); return(result); } - if ( params.size() == 3 ) - { + + if (params.size() >= 3) { description = params[2].get_str(); - if ( description.size() > 4096 ) - { + if (description.size() > 4096) { ERR_RESULT("Token description must be <= 4096 characters"); return(result); } } - hex = CreateToken(0,supply,name,description); - if ( hex.size() > 0 ) - { + + if (params.size() == 4) { + nonfungibleData = ParseHex(params[3].get_str()); + if (nonfungibleData.size() > 10000) // opret limit + { + ERR_RESULT("Non-fungible data must be <= 10000"); + return(result); + } + } + + hextx = CreateToken(0, supply, name, description, nonfungibleData); + if( hextx.size() > 0 ) { result.push_back(Pair("result", "success")); - result.push_back(Pair("hex", hex)); - } else ERR_RESULT("couldnt create transaction"); + result.push_back(Pair("hex", hextx)); + } + else + ERR_RESULT(CCerror); return(result); } UniValue tokentransfer(const UniValue& params, bool fHelp) { - UniValue result(UniValue::VOBJ); std::string hex; int64_t amount; uint256 tokenid; - if ( fHelp || params.size() != 3 ) + UniValue result(UniValue::VOBJ); + std::string hex; + int64_t amount; + uint256 tokenid; + + CCerror.clear(); + + if ( fHelp || params.size() != 3) throw runtime_error("tokentransfer tokenid destpubkey amount\n"); if ( ensure_CCrequirements() < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); + tokenid = Parseuint256((char *)params[0].get_str().c_str()); std::vector pubkey(ParseHex(params[1].get_str().c_str())); //amount = atol(params[2].get_str().c_str()); amount = atoll(params[2].get_str().c_str()); // dimxy changed to prevent loss of significance - if ( tokenid == zeroid ) - { + if( tokenid == zeroid ) { ERR_RESULT("invalid tokenid"); return(result); } - if ( amount <= 0 ) - { + if( amount <= 0 ) { ERR_RESULT("amount must be positive"); return(result); } - hex = TokenTransfer(0,tokenid,pubkey,amount); - if (amount > 0) { - if ( hex.size() > 0 ) - { - result.push_back(Pair("result", "success")); - result.push_back(Pair("hex", hex)); - } else ERR_RESULT("couldnt transfer assets"); - } else { - ERR_RESULT("amount must be positive"); + + hex = TokenTransfer(0, tokenid, pubkey, amount); + + if( !CCerror.empty() ) { + ERR_RESULT(CCerror); + } + else { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); } return(result); } @@ -7786,3 +7809,4 @@ UniValue test_heirmarker(const UniValue& params, bool fHelp) cp = CCinit(&C, EVAL_HEIR); return(FinalizeCCTx(0, cp, mtx, myPubkey, 10000, opret)); } +